在TypeScript中为过载函数定义工厂

时间:2018-04-16 08:52:01

标签: 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,
    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?

您可以通过TS playground

查看错误详情

2 个答案:

答案 0 :(得分:1)

问题是,由于replacer是来自所有replacer重载的stringify参数类型的并集,因此它实际上与任何重载都不兼容。选择过载打字稿时会尝试找到最符合参数的重载,因为你的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);
    }
}

答案 1 :(得分:0)

受@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)