javascript - 如何在 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
,n
和b
都有一个string | number | boolean
. 而不是每个都有可能的最窄类型。(TypeScript Playground 链接)
我怎样才能使缩小工作?
解决方案
当你调用一个泛型函数时,它的类型参数是在那个时候指定的。这要么由调用者使用尖括号(例如,factory<MyType, MyKey>(someValueOfMyType)
)手动完成,要么编译器从传递给函数的参数和调用函数的上下文中推断出它们。根据您的定义,调用
const create = factory({
string: () => 'A string of text.',
number: () => 42,
boolean: () => true
});
必须推断T
和K
。 T
很容易推断,因为传入的参数create
是 type T
。T
也是如此{string: ()=>string, number: ()=>number, boolean: ()=>boolean}
。但是在那个调用中没有任何地方K
可以推断出来。没有 typeK
的参数,上下文只是将返回值保存到名为 的变量create
中。因此推理失败。在这种情况下,编译器会选择最广泛的工作类型,即它的约束。这意味着K
推断为keyof T
which 变为"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
好的,希望有帮助。祝你好运!
推荐阅读
- c# - 在不使用临时变量的情况下交换两个数字
- wordpress - get_taxonomy_labels 只返回标签
- spring-boot - 为什么 get 请求在 Spring Boot 中给出空响应?
- spring-boot - 基于 Open API 3 继承的不同示例
- html - 如何使用带有图像作为背景的 html 和 css 使部分的顶部和底部向内弯曲而不是向外弯曲?
- javascript - Removing appended 'n' from BigInt()
- java - 使用 Jayway 在 JAVA 中解析具有 Escape 和 Unescape 字符的 JSON
- java - 电话状态监听器的广播接收器在几次通话后不起作用
- r - 对 R6 对象使用“with”
- vba - 填写 Word 表格