Javascript 定义TypeScript中重载函数的工厂

Javascript 定义TypeScript中重载函数的工厂,javascript,typescript,Javascript,Typescript,假设使用我自己的函数包装JSON.stringify: declare function stringify( value: any, replacer?: (key: string, value: any) => any, space?: string | number ): string; declare function stringify( value: any, replacer?: (number | string)[] | null,

假设使用我自己的函数包装JSON.stringify:

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!
}
如何创建我自己的方法myStringify重用JSON.stringify


您可以通过检查错误详细信息。问题在于,由于
replacer
是来自所有
stringify
重载的
replacer
参数类型的并集,因此它实际上与任何重载都不兼容。选择重载类型时,脚本将尝试查找与参数最匹配的重载,因为您的
replacer
既不兼容第一个重载(该重载需要函数,您的参数也可以是数组)也不兼容第二个重载(该重载需要数组,您的参数可以是函数)过载解决过程将失败

您可以自己添加这两个重载,也可以使用类型保护来调用同一个函数,或者只使用断言:

// 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);
    }
}

受@Titian Cernicova Dragomir答案的启发,我找到了一个通用解决方案供参考。试试myFun2的做法

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)
声明函数s(r:()=>string):string;
声明函数s(r:()=>number):number;
常数a=s(()=>'123')///sring
常数b=s(()=>123)//个数
函数myFun(r:(()=>字符串)|(()=>数字)){
类型R=返回类型扩展字符串?字符串:编号
返回s(r)//TS错误:类型兼容!
}
const c=myFun(()=>'123')//字符串,由我们声明函数的顺序决定
const d=myFun(()=>123)//字符串,完全错误
函数myFun2(r:()=>string):string;
函数myFun2(r:()=>number):number;
函数myFun2(r:(()=>string)|(()=>number)):string | number{
类型R=返回类型扩展字符串?字符串:编号
返回s(r为任意)为任意r
}
常数e=myFun2(()=>123')
常数f=myFun2(()=>123)

解决方案二似乎不是通用的,如果replacer有两个不同的函数签名,我们就不能这样处理它。解决方案一有一个潜在的问题,即如果这两个重载具有不同的返回类型,我们将丢失准确的返回类型。@zheeng解决方案1确实绕过了类型安全性,并且可能会中断。如果联合包含更多的函数,解决方案2可能会出现问题,但是您不能为特定的函数签名键入guard,幸运的是在这种情况下只有一个。