从 TS4.8 开始,TypeScript 无法使用控制流分析 https://www.typescriptlang.org/docs/handbook/2/narrowing.html#control-flow-analysis缩小或重新限制generic https://www.typescriptlang.org/docs/handbook/2/generics.html类型参数。所以在createValueOfObjKey(key)
where key
是泛型类型K extends keyof Obj
,你可以检查key
via switch
,这将缩小类型key
... but K
本身将顽固地保持不变。因此编译器不知道字符串或数字是否可以分配给Obj[K]
它抱怨。
这是 TypeScript 目前缺少的功能。有一个悬而未决的问题微软/TypeScript#33014 https://github.com/microsoft/TypeScript/issues/33014要求添加对您编写的代码的支持。除非实施该措施或类似措施,否则只有解决方法。
最简单的解决方法就是assert https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions你所做的事情是正确的。它是类型安全性最低的,但不需要您对实现进行太多更改:
function createValueOfObj<K extends keyof Obj>(key: K): Obj[K] {
switch (key) {
case "foo":
return 0 as Obj[K]; // assert
case "bar":
return "" as Obj[K]; // assert
}
throw new Error("UNREACHABLE"); // compiler can't see exhaustive so you need this
}
如果您想要更多的类型安全性,那么您需要编写一些编译器可以验证准确的内容。为索引访问类型 https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html like T[K]
,如果您采用该类型的对象,编译器只能轻松验证您是否生成了该类型的有效值t
和类型键k
并返回值t[k]
。也就是说,您可以执行索引访问手术获得可验证的索引访问type.
所以你可以写
function createValueOfObj<K extends keyof Obj>(key: K): Obj[K] {
return {
foo: 0,
bar: ""
}[key]; // okay
}
编译没有错误,万岁!
如果您担心这会迫使您提前提出所有可能的输出值,您可以通过延迟实现此查找对象getters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get而不是常规属性:
function createValueOfObjKey<K extends keyof Obj>(key: K): Obj[K] {
return {
get foo() { return 0 },
get bar() { return "" }
}[key]; // okay
}
编译也没有错误,现在只有与实际对应的代码key
将被执行。
让我们验证一下它是否有效:
const foo = createValueOfObjKey("foo");
console.log(foo.toFixed(2)) // "0.00"
const bar = createValueOfObjKey("bar");
console.log(bar.toUpperCase()) // ""
看起来不错。
Playground 代码链接 https://www.typescriptlang.org/play?ts=4.8.2#code/JYOwLgpgTgZghgYwgAgPICMBWyDeAoZZGAe2IC5kQBXAW3WgG4Dl04oKBnMKUAcyYC+eYXhhUQCMMGIhkCKBDiQAanAA2VCKhgZMAQQ4doYADwBpZBAAekEABMOyANYQAnsRhosAPgAUL1wozAEoKXQBtMwBdXGYOAHdgMAQAC2R-N2DYwkIEOCNkACISYkKyZhzkBTAqKFkABmR8r0xIqKZKvILC1igyipzq2tlCwqbHCOiO5CFCMBSoYnjKCGWAUShFqF9CgFUAOQAlNb0AYQAJPQAhABk1wuDBYTEJKRk5BSUIVQ0tHSwAJJ2KzmSw2CD2RwBDwtPwBIKhFptbJVCA1OoowglCj1AA0A16FFGzAE4QC7TwQhEL0k0lk8kUKnUmm0ujMblB1lsDmcbhhujhbgRYSwyPwhCGGPFOV4aKIpF8WRwqPRDRm+MqsrALDYitwKuGRTGsxmZLcFKpCBkXHlxGQAF4Pozvsy-my3DsSg8mFaQBxiGoIAA6NTEXi+EpBsDEABiwCsEDsvgATMEsgB6dNFepB+r1Qp4X023oOp1fH4s-6YdmuHa9b2F60B4Oh8O9KPEXYABy70FO+QgiozWeJQA