如何在 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')
但在这种情况下 s
、n
和 b
都具有 string | number | boolean
的类型。而不是每个都有尽可能窄的类型。 (TypeScript Playground link)
如何进行缩小?
当你调用泛型函数时,它的类型参数是在那个时候指定的。这要么由调用者使用尖括号(例如 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
类型的参数,上下文只是将 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
好的,希望对您有所帮助。祝你好运!
我正在尝试获取一个适用于 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')
但在这种情况下 s
、n
和 b
都具有 string | number | boolean
的类型。而不是每个都有尽可能窄的类型。 (TypeScript Playground link)
如何进行缩小?
当你调用泛型函数时,它的类型参数是在那个时候指定的。这要么由调用者使用尖括号(例如 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
类型的参数,上下文只是将 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
好的,希望对您有所帮助。祝你好运!