您好,打字稿专家
有人可以解释为什么下面的代码在第16行中给我一个错误,但在13行中却给我一个错误吗?这是预期的还是缺少的功能?
代码
interface Config {
// There need to be different types in here for the error to occur
A: number
B: string
}
type union = "A" | "B"
var Global = {A: 1, B: "Hello"} as Config
function foo<K extends union>(key: K, x: union) {
if (x === "A"){
Global[x].toFixed()
}
if (key === "A"){
Global[key].toFixed()
}
}
答案 0 :(得分:1)
可能是缺少的功能。
当您检查(x === "A")
时,它会作为值x
的类型上的type guard,导致其从联合类型"A"|"B"
缩小为{{1 }}通过control flow analysis。
不幸的是,TypeScript中的泛型类型参数不会通过控制流分析来缩小;有关更多信息,请参见microsoft/TypeScript#13995。因此,例如,检查"A"
不会 将 (key === "A")
的类型缩小为K
。当您具有相同的通用类型的多个值时,这种限制是有意义的:
"A"
显然,检查function foo<K extends Union>(key: K, x: Union, key2: K) {
if (key2 === "A") {
Global[key].toFixed(); // error!
}
}
的值应该对key2
的类型没有影响,因此编译器保守地不认为应该缩小key
的范围。这是microsoft / TypeScript#13995中的基本问题,并且在应该安全地进行这种范围缩小的情况下,提出了有关如何处理它的建议的多个相关问题。到目前为止,还没有任何东西成为语言。
但这并不是全部内容;一个人可能会反驳:好吧,也许您不能将 type参数 K
的范围从K
缩小到K extends Union
,但是< em>确定,您可以将 value K extends "A"
的类型从key
缩小为K
或"A"
({{3 }}),这将使K & "A"
成功:
Global[key].toFixed()
我现在对此还没有很好的答案。我最终看到的大多数问题最终都被引用到microsoft / TypeScript#13995。 ?♂️
我能得到的最接近的完整答案是,似乎只能使用if (key === "A") {
Global[key as (K & "A")].toFixed(); // okay
}
或a === b
或typeof a === "string"
或a instanceof B
这样的内置类型防护过滤并集或将a in b
或string
缩小为字符串或数字文字,但是从不会生成交集类型。我已经为number
类型防护提供了intersection type来产生一些交集,但尚未实现。因此,这可能是两个缺少的功能:
因此,解决方法:显然,此示例最简单的方法是将泛型变量的值重新分配为联合类型变量:
a in b
或者,您可以像上面的const k: Union = key;
if (k === "A") {
Global[k].toFixed();
}
一样使用asked before, see microsoft/TypeScript#21732,或仅使用as (K & "A")
:
as "A"
或者,如果经常发生这种情况,则可以编写自己的type assertion函数,因为用户定义的类型防护 do 在后续项的if (key === "A") {
Global[key as (K & "A")].toFixed(); // okay
Global[key as "A"].toFixed(); // okay
}
分支中产生交集控制流:
true
希望其中之一对您有所帮助。好的,祝你好运!
答案 1 :(得分:0)
我不确定为什么会发生该错误,但是我确定有人有很好的解释。这是通过将intersection types与泛型一起使用来实现相同目的的另一种方法:
interface Config {
// There need to be different types in here for the error to occur
A: number
B: string
}
type Union = keyof Config;
const Global: Config = { A: 1, B: "Hello" };
function foo<K>(key: K & Union, x: Union) { // Use intersection type instead of inheritance
if (x === "A"){
Global[x].toFixed();
}
if (key === 'A'){
Global[key].toFixed()
}
}
也看到以下问题:Difference between extending and intersecting interfaces in TypeScript?