typescript - 使用字符串枚举来扩展 keyof
问题描述
在使用字符串枚举来索引类型时,我很难理解它们的行为方式。似乎有时 TS 会识别出字符串枚举的值是keyof
某种类型的扩展,而其他时候则不会。为了显示:
enum Key {
FOO = "foo",
}
type MyObj = {
foo: string
}
因此,一个类型的属性的枚举(即,您可以索引obj:MyObj
using obj[Key.FOO]
)。
现在我想定义另一种类型,它将在 MyObj 类型中查找一个值以确定它是否是该类型的键,如果是,则使用该键值的类型:
type FunctionReturn<T extends Record<string, any>,U extends keyof T> = {
returnValue: U extends keyof T ? T[U]: never
}
但是,如果我按如下方式使用该类型,则会出现错误。
function myFunction(value: MyObj[Key.FOO]):FunctionReturn<MyObj, Key.FOO>{
return {returnValue: "a string"}
}
即,TS 在键入 returnValue 时找不到满足的U extends key of T
(其中 U 是枚举成员),即使它接受它满足 FunctionReturn 类型的约束。
如果我将实际的字符串文字作为参数传递,错误就会完全消失。
function myOtherFunction(value: MyObj[Key.FOO]):FunctionReturn<MyObj, "foo">{
return {returnValue: "a string"}
这是为什么?在我看来,如果 TS作为通用约束中Key.FOO
的有效扩展除外keyof MyOb
,它也应该满足条件。我错过了什么?
这里是游乐场。
非常感谢,一如既往。
解决方案
你可以使用这样的技巧:
enum Key {
FOO = "foo",
}
type MyObj = {
foo: string
}
/*
I've added V generic parameter to tell TS to use U as keyof T, not as something else.
Now it works as expected.
*/
type FunctionReturn<T extends Record<string, any>,U extends keyof T, V = T[U]> = {
returnValue: U extends keyof T ? V: never
}
function myFunction(value: MyObj[Key.FOO]):FunctionReturn<MyObj, Key.FOO>{
return {returnValue: "a string"}
}
function myOtherFunction(value: MyObj[Key.FOO]):FunctionReturn<MyObj, "foo">{
return {returnValue: "a string"}
}
一些解释:
让我们从头开始。该函数期望U extends keyof T
作为第二个类型变量。这意味着您可以传递任何可分配给的东西,keyof T
也意味着U
在函数执行中可以是任何可分配给的类型keyof T
。当您通过Key.FOO
asU
时,TS 检查可分配性,keyof T
它将被通过。然后,TS 推断U
as的类型Key
(这很重要)。然后,由于U extends keyof T
将解析为 true,TS 将解析T[U]
为T[Key]
. 然后,解析T[key]
TS 将与约束 相交keyof T
,像这样T[Key & keyof T]
,反过来解析为T[never]
。最有趣的是,这样的交集Key.FOO & "foo"
会被解析到never
。为什么要never
? 好吧,可能是因为严格的规则,就像 TS 中除 enum 本身之外的任何其他类型的交集枚举将被解析为never
.
为什么建议的方法有效:
当你FunctionReturn
这样使用时FunctionReturn<MyObj, Key.FOO>
,TS 首先解析泛型参数:
T = MyObj,
U = Key.FOO,
V = MyObj[Key.FOO] = string
因为真正的分支内部没有索引访问,所以不需要以某种方式解决它,TS 只是在真正的分支中使用类型字符串。
推荐阅读
- java - 具有自定义宽度和高度的 PdfBoxRenderer
- javascript - 如何在带有 v-for 指令的组件内使用三元运算符?
- python-3.x - Selenium python webscraper - 如何仅选择网页列表中更改的第一个文档?
- git - 我错误地发出了一个 git branch --set-upsteam-to,那是做什么的?
- javascript - 'prop-types' 应该列在项目的依赖项中,而不是 devDependencies
- python - 循环通过多个 API 链接获取数据?似乎从一个链接带回数据
- python - 如何使用函数定义的简单断言语句来接受 2 个参数中的 1 个,但不能同时接受关键字参数?
- typescript - 如何使用 .map 和 .filter 将两个数组合并为一个
- html - 给它一个边框后按钮不再可点击
- python - 如何让笛卡尔积过滤出具有重复结果的元组对?