如何在 TypeScript 中输入可配置的工厂函数?

How to type a configurable factory function in TypeScript?

我正在尝试获取一个适用于 return 各种 return 类型的工厂函数的基本示例,具体取决于它传递给它的 key 字符串…

const factory = <
    T extends Record<string, () => any>,
    K extends keyof T
>(options: T) => {
    return (key: K): ReturnType<T[K]> => {
        return options[key]()
    }
}

const create = factory({
    string: () => 'A string of text.',
    number: () => 42,
    boolean: () => true
})

const s = create('string')
const n = create('number')
const b = create('boolean')

但在这种情况下 snb 都具有 string | number | boolean 的类型。而不是每个都有尽可能窄的类型。 (TypeScript Playground link)

如何进行缩小?

当你调用泛型函数时,它的类型参数是在那个时候指定的。这要么由调用者使用尖括号(例如 factory<MyType, MyKey>(someValueOfMyType))手动完成,要么编译器从传递给函数的参数和调用函数的上下文中推断出它们。根据您的定义,调用

const create = factory({
    string: () => 'A string of text.',
    number: () => 42,
    boolean: () => true
});

必须推断 TKT很容易推断,因为传给create的参数是T类型的。所以 T{string: ()=>string, number: ()=>number, boolean: ()=>boolean}。但是在那个调用中没有任何地方可以推断 K 。没有 K 类型的参数,上下文只是将 return 值保存到名为 create 的变量中。因此推理失败。在这种情况下,编译器会选择最广泛的工作类型,即它的 constraint。这意味着 K 被推断为 keyof T 变成 "string" | "number" | "boolean"。因此 create 具有类型

// const create: (key: "string" | "number" | "boolean") => string | number | boolean

你不想要的。


您实际上只希望在调用 factory() 时指定 T,而不希望在调用 create() 之前指定 K。例如,一旦你调用 create("string"),那么你就知道 K 应该是 "string"。由于在调用函数时指定了通用函数类型参数,因此您希望 create() 本身是一个通用函数,其中 K 是其类型参数。

所以这里的解决方案是将 T 留在原处,但是将 K 泛型从外部函数移动到它 returns:

的函数
const factory = <
    T extends Record<string, () => any>>(options: T) => {
    return <K extends keyof T>(key: K): ReturnType<T[K]> => {
        return options[key]()
    }
}

现在,当您调用 factory() 时,它 return 是一个通用函数:

const create = factory({
    string: () => 'A string of text.',
    number: () => 42,
    boolean: () => true
})

/* const create: <K extends "string" | "number" | "boolean">(key: K) => ReturnType<{
    string: () => string;
    number: () => number;
    boolean: () => boolean;
}[K]> */

然后它会按照您的预期运行:

const s = create('string') // string
const n = create('number') // number
const b = create('boolean') // boolean

好的,希望对您有所帮助。祝你好运!

Link to code