gpt4 book ai didi

typescript - 防止条件分支中的 Typescript 变窄

转载 作者:行者123 更新时间:2023-12-03 23:03:13 26 4
gpt4 key购买 nike

在进行控制流分析时,Typescript 似乎正在缩小联合:

type Foo = {x: number}
type Bar = {x: number, y: string}


function f(arg: Foo | Bar) {

// Type of a is Foo | null
const a = true ? arg : null
}
我假设 a 的类型是 Foo | null因为 FooBar 的子类型这里的最小安全类型也是如此。我如何防止这种缩小的发生?例如,如果我想做以下事情怎么办:
function p(arr: (Foo | Bar)[]) {

const b = arr.length ? arr[0] : null;

// Treat b as Foo | Bar | null
if (b && 'y' in b) {

// Error: Property 'y' does not exist on type 'never'
console.log(b.y);
}
}

最佳答案

由于每Bar也是Foo ,工会Foo | Bar相当于只是 Foo ,根据可以为其分配哪些值。从某种意义上说,Foo | Bar仅包含 Foo 的信息,很像 string | "hello"包含与仅 string 相同的信息.
当编译器显式转换时 Foo | BarFoo ,这称为“子类型减少”(如在 microsoft/TypeScript#4537 中实现)。这听起来不错,但有时很烦人,因为从技术上讲,| Bar没有给你更多关于什么类型的值可能存在的信息,从实用的角度来说,它给了你一个提示,也许是 y属性可用于确定它是什么。

in operator is treated as a type guard (在 microsoft/TypeScript#15256 中实现)其中属性的存在用于缩小联合类型。这很有用,但不合理。务实地说,您希望如果 {a: 0} | {b: 1}包含 a属性然后它是一个 {a: 0} ,但从技术上讲,您不能假设。毕竟值{a: "oops", b: 1}是有效的 {a: 0} | {b: 1}但不是有效的 {a: 0} .仅仅存在 a属性在技术上并不能告诉您足够的缩小类型,但从实用上讲确实可以。

这两种处理属性和联合的方式相互矛盾,在像你这样的情况下,它们相互作用很差。
第一 Foo | Bar减少到 Foo (技术上正确),不知道有 y属性(property)。然后你使用 in运算符(operator)检查 y属性,它决定它不能是 Foo完全没有,因为它缺乏已知的 y属性(技术上不正确)。
有问题 microsoft/TypeScript#37518这似乎正是这个,但它只是标记为“需要调查”。我很确定这本身只是一个设计限制而不是一个错误。每个功能都按宣传的那样工作。

那么可以做什么呢?有几种方法。一种方法是更改​​ arr 的元素类型来自 Foo | Bar到不相交的联合或非联合类型,例如以下任一类型:

type FooNotBar = { x: number, y?: never };
function q(arr: (FooNotBar | Bar)[]) {
const b = arr.length ? arr[0] : null;
if (b && typeof b.y !== "undefined") {
console.log(b.y.toUpperCase()); // okay
}
}

type FooOrBar = { x: number, y?: string };
function r(arr: (FooOrBar)[]) {
const b = arr.length ? arr[0] : null;
if (b && typeof b.y !== "undefined") {
console.log(b.y.toUpperCase()); // okay
}
}
在这里, FooNotBarFoo这不是 Bar因为它不能定义 y属性(property)。所以 FooNotBar | Bar可以使用 y属性来确定它是哪种类型。等效地, FooOrBarFoo这是一个 Bar或非 Bar因为 y要么是 string否则它不会被定义。同样,您可以查看 y属性来确定。请注意,这些都不适用于 in但是,类型保护,因为它不再可以仅使用 y 的存在。键来确定您拥有哪种类型。

另一种方法是保留您的 Foo | Bar并让它折叠到 Foo由编译器,但要使用 user-defined type guard function给出你自己想要的行为而不是默认的 in类型保护行为:
const isBar = (x: Foo): x is Bar => 'y' in x;

function s(arr: (Foo | Bar)[]) {
const b = arr.length ? arr[0] : null;
if (b && isBar(b)) {
console.log(b.y.toUpperCase()); // okay
}
}
在这里,您是说“如果我调用 isBar(foo) ,则 true 结果意味着 foo 可以被视为 Bar ”。 isBar()的实现只是使用 in ,但签名 isBar()传达意图。

Playground link to code

关于typescript - 防止条件分支中的 Typescript 变窄,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64271752/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com