首页 > 解决方案 > 如何在 TypeScript 中键入可配置的工厂函数?

问题描述

我正在尝试获取一个适用于工厂函数的基本示例,该函数根据key传递给它的字符串返回各种返回类型……</p>

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')

但在这种情况下s,nb都有一个string | number | boolean. 而不是每个都有可能的最窄类型。(TypeScript Playground 链接

我怎样才能使缩小工作?

标签: javascripttypescriptgenericsfactory

解决方案


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

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

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

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

你不想要的。


你真的只想T在你打电话的时候被指定,你factory()不想K在你打电话之前被指定create()。例如,一旦您调用create("string"),那么您就知道K应该是"string"。并且由于在调用函数时指定了泛型函数类型参数,因此您希望create()自己成为泛型函数,K其类型参数在哪里。

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

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

现在,当您调用factory()它时,它会返回一个通用函数:

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

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

链接到代码


推荐阅读