TypeScript 当字段键为数字时,歧视性工会无法工作,

dldeef67  于 5个月前  发布在  TypeScript
关注(0)|答案(9)|浏览(122)

🔎 搜索词

  • 区分工会,"discriminated-unions"
  • "对象中键为数字", "number key"
  • "类型保护"

🕗 版本与回归信息

  • 这是一次崩溃

⏯ Playground链接

https://www.typescriptlang.org/play?#code/C4TwDgpgBAglC8UB2BXAtgIwgJwNoF0oAfKAZ2GwEskBzAgKHtEigCEEoBvKABgC5k6LNgA0UAIYCAjFAC+xLrwHkqtMZKgByKZrmMAJhADGAG3HZoRgPZJyEgTHqHT5yzbsYBrRpQBmUAApmCCt-cVweQnhorVRMHE0ASi56KDSoa1srEwgAOhMrGgDxRNT0gHpy9Or0gD0AfnpZej9A4NCoDFxNcU0omM044SSU6szSbLyCooxS6sqamoamlv8g8BD-Ls0ePoQBoYTkzjK08cn8woDZ06gFxbrG5tb1yA6uyP3EQaEj0fTzjlLjM5hUqg80stmkA

💻 代码

type A = number[] | string[]

type B = { 0: number, a: 1 } | { 0: string, a: '1' }

declare const a: A
declare const b: B

if (typeof a[0] === 'number') {
    console.log(a)
    //          ^? A
}
if (typeof b['a'] === 'number') {
    console.log(b)
    //          ^? { 0: number, a: 1 }
}
if (typeof b['0'] === 'number') {
    console.log(b)
    //          ^? B
}
if (typeof b[0] === 'number') {
    console.log(b)
    //          ^? B
}

🙁 实际行为

无法将 b 作为联合类型 B 的选项进行类型保护。

🙂 预期行为

在用 typeof b['0'] === 'number'b 进行类型保护时,将其视为 { 0: number, a: 1 }

关于问题的附加信息

8wtpewkr

8wtpewkr1#

文档似乎没有规定键必须是字符串。

z9gpfhce

z9gpfhce2#

这与键是否为字符串无关-判别式必须是字面类型的。numberstring 不是有效的判别式,而且也无法通过 typeof 进行判别式检查。

gev0vcfq

gev0vcfq3#

typeof 在某些情况下可以工作,但我完全不理解它。

b1zrtrql

b1zrtrql5#

看起来只有特定的联合类型会触发这个bug。在这个例子中,只有数字和字符串的联合触发了bug,可能是因为它被推断为 number | string 。当我将其更改为另一个基本类型的联合或字面量类型时,Discriminated 可以成功工作。

bvjxkvbb

bvjxkvbb6#

boolean 在内部被视为 true | false,它是字面类型的,并使属性具有区分性。

sg24os4d

sg24os4d7#

这是我第一次看到 typeof 可以用于缩小一个被区分的联合体。这是一种高度非正统的做法(在这种情况下,甚至具有误导性)。维护者一直说,被区分的联合体缩小只适用于直接值比较(即 x[0] === 'foo' )。

h7wcgrx3

h7wcgrx38#

通过在区分联合体上使用类型缩小是通过寻找看起来像判别属性的属性来实现的...但是 string[]number[] 没有这样的属性!这是可以做到的,但现在还不是正确的方法。

0g0grzrc

0g0grzrc9#

这里有一个奇怪的现象,如果不是 union type 只有 string[] 的情况,这个问题有一个部分解。

type A = ({ [0]: number } & number[]) | ({ [0]: boolean } & boolean[])

declare const a: A

if (typeof a[0] === 'number') {
    console.log(a)
    //          ^? { [0]: number } & number[]
}
if (typeof a[0] === 'boolean') {
    console.log(a)
    //          ^? { [0]: boolean } & boolean[]

Playground Link
如果我们用 string[] 替换 boolean[],那么就会产生一个问题。

type A = ({ [0]: number } & number[]) | ({ [0]: string } & string[])

declare const a: A

if (typeof a[0] === 'number') {
    console.log(a)
    //          ^? A
}
if (typeof a[0] === 'string') {
    console.log(a)
    //          ^? A

Playground Link

相关问题