我正在尝试为工厂函数工作的基本示例,该函数根据传递给它的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')
但是在这种情况下,s
,n
和b
的类型均为string | number | boolean
。而不是每个类型都有最窄的类型。 (TypeScript Playground link)
如何进行收窄工作?
答案 0 :(得分:1)
调用泛型函数时,此时将指定其类型参数。这可以由调用者使用尖括号(例如factory<MyType, MyKey>(someValueOfMyType)
)手动完成,也可以由编译器根据传递给函数的参数以及调用函数的上下文来推断出来。根据您的定义,调用
const create = factory({
string: () => 'A string of text.',
number: () => 42,
boolean: () => true
});
必须推断T
和K
。 T
很容易推断,因为传递到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
好的,希望能有所帮助。祝你好运!