Reputation: 682
Given a situation that wrapping the JSON.stringify with my own function:
declare function stringify(
value: any,
replacer?: (key: string, value: any) => any,
space?: string | number
): string;
declare function stringify(
value: any,
replacer?: (number | string)[] | null,
space?: string | number
): string;
function myStringify(
data: object,
replacer: ((key: string, value: any) => any) | (number | string)[] | null,
space: string | number,
) {
return stringify(data, replacer, space); // TS error: type is compatible!
}
How to create my own method myStringify reuse the JSON.stringify?
You can check the error detail through the TS playground
Upvotes: 2
Views: 560
Reputation: 682
Inspired by @Titian Cernicova-Dragomir's answer, I found a generic solution for reference. Try the way myFun2 does.
declare function s(r: () => string): string;
declare function s(r: () => number): number;
const a = s(() => '123') // sring
const b = s(() => 123) // number
function myFun(r: (() => string) | (() => number)) {
type R = ReturnType<typeof r> extends string ? string : number
return s(r)// TS error: type is compatible!
}
const c = myFun(() => '123') // string, is decide by the order we declare the function 's'
const d = myFun(() => 123) // string, totally wrong
function myFun2(r: () => string): string;
function myFun2(r: () => number): number;
function myFun2(r: (() => string) | (() => number)): string | number {
type R = ReturnType<typeof r> extends string ? string : number
return s(r as any) as any as R
}
const e = myFun2(() => '123')
const f = myFun2(() => 123)
Upvotes: 0
Reputation: 249536
The problem is that since replacer
is a union of the types of the replacer
parameter from all stringify
overloads it is in fact compatible with none of the overloads. When selecting the overload typescript will try to find the overload that best matches the parameters, since your replacer
is compatible with neither the first overload (that overload expects a function, your parameter can also be an array) or the second overload (that overload expects an array, your parameter can be a function) the overload resolution process will fail.
You can add the two overloads yourself, or you can use a type guard to essentially call the same function, or you can just use an assertion:
// assert to any
function myStringify(
data: object,
replacer: ((key: string, value: any) => any) | (number | string)[] | null,
space: string | number,
) {
return JSON.stringify(data, replacer as any, space)
}
// use a type gurad, but it seems overkill to do so.
function myStringify(
data: object,
replacer: ((key: string, value: any) => any) | (number | string)[] | null,
space: string | number,
) {
if(Array.isArray(replacer)) {
return JSON.stringify(data, replacer, space);
} else if(typeof replacer === "function") {
return JSON.stringify(data, replacer, space);
}
}
Upvotes: 1