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

问题描述 投票:1回答:1

我正在尝试为工厂函数工作的基本示例,该函数根据传递给它的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

如何进行收窄工作?

javascript typescript generics factory
1个回答
0
投票
调用泛型函数时,此时将指定其类型参数。这可以由调用者使用尖括号(例如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的参数,上下文只是将返回值保存到名为create的变量中。因此,推断失败。在这种情况下,编译器会选择最有效的类型,即其constraint。这意味着将K推断为keyof T,成为"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

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

Link to code

© www.soinside.com 2019 - 2024. All rights reserved.