TypeScript 对于无效的区分联合体的混淆类型检查器错误消息

jfgube3f  于 4个月前  发布在  TypeScript
关注(0)|答案(7)|浏览(59)

Bug报告

🔎 搜索词

  • Tagged union
  • Discriminated union
  • 令人困惑/不清楚的错误信息

🕗 版本与回归信息

此问题在Playground中可用的最旧版本(v3.3)和当前版本(v4.9)上发生。

  • 在所有我尝试过的版本中,我都检查了关于区分联合和标记联合的FAQ条目。

⏯ Playground链接

包含相关代码(v4.9.4)的Playground链接。

💻 代码

type DiscriminatedUnion =
    | { tag: 'a', data: string }
    | { tag: 'b', data: boolean }
    | { tag: 'c', data: number }

const myUnion: DiscriminatedUnion = {
    tag: 'a',
    data: true,  // <-- wrong, should be string
}

🙁 实际行为

类型检查器的错误:

Type '{ tag: "a"; data: true; }' is not assignable to type 'DiscriminatedUnion'.
  Type '{ tag: "a"; data: true; }' is not assignable to type '{ tag: "c"; data: number; }'.
    Types of property 'tag' are incompatible.
      Type '"a"' is not assignable to type '"c"'.

🙂 预期行为

应该从类型检查器得到错误,但不是 Type '"a"' is not assignable to type '"c"'. 。在这个例子中,“c”与问题无关,问题是 data 应该是一个 string ,但实际上是一个 boolean
我不确定这种情况下的确切错误信息应该是什么样的,大致为 Type 'boolean' is not assignable to type 'string'.

kuhbmx9i

kuhbmx9i1#

tag 是一个具有三种可能类型的字段,而 data 是一个具有三种可能类型的字段。在确定哪一个是“真正的”判别式方面,实际上没有一种有原则的方法来选择其中一个而不是另一个。寻找这个的代码会选择它找到的第一个看起来像真正判别式的字段,我认为将其转换为排名算法与直接过滤相比并不值得。

8e2ybdfx

8e2ybdfx2#

在确定哪个是"真正的"判别式方面,没有一个真正有原则的方法来选择其中一个而不是另一个。

在这种情况下,它不应该总是选择 tag 作为判别式,因为 data 不是字面类型的吗?而且,如果它选择 data 因为 booleantrue | false ,那么它似乎很奇怪地选择了 datanumber 的候选项。

0yycz8jy

0yycz8jy3#

然后,似乎奇怪的是它选择了一个数据是数字的候选项。
是的,这部分很奇怪。我错过了这部分。
我想表达的是,这两个消息都有平等的地位来声称自己是正确的:

  • 根据数据类型,判别式是错误的,所以'a'应该是'b'
  • 根据判别式,数据类型是错误的,所以布尔值应该是一个字符串

因此,在这种情况下,提到c/number是不好的做法。

mqxuamgl

mqxuamgl4#

@fatcerberus,我猜类型检查器正在做的事情是:

Is it '{ tag: 'a', data: string }'? No
Is it '{ tag: 'b', data: boolean }'? No
Is it '{ tag: 'c', data: number }'? No - show error to user

所以看起来错误是用"c"显示的,因为这恰好是最后要检查的情况。
如果我理解正确的话,可能无法(或不切实际)找到一种启发式方法来知道哪些字段是判别标准。由于判别标准/标签与F#/OCaml等相比是“隐式的”,因此编译器不清楚哪个字段实际上是标签。如果多个字段可以作为判别标准,可能会出现组合爆炸的可能性(这是可能的吗?)
理想情况下,编译器的工作原理如下:

  1. 确定联合类型是否为带判别标准的联合类型
  2. 将判别标准与正在检查的类型进行匹配
  3. 如果判别标准匹配成功,则检查其余字段的类型
    但这可能不是一个实际可行的工作流程,具体取决于需要检查的内容的可能性。
vx6bjr1n

vx6bjr1n5#

寻找用于错误报告(以及其他)目的的判别属性是有逻辑的,例如这段代码将根据标签给出不同的错误信息

type DiscriminatedUnion =
    | { tag: 'a', alpha: string }
    | { tag: 'b', beta: boolean }
    | { tag: 'c', gamma: number }

const myUnion: DiscriminatedUnion = {
    tag: 'b'
}

但是(根据用户反馈!),判别属性不仅仅局限于字面类型:

type DiscriminatedUnion =
    | { tag: string, astring: string }
    | { tag: number, anumber: boolean }
    | { tag: boolean, aboolean: number }

const myUnion: DiscriminatedUnion = {
    tag: 42
}

因此,在OP中,datatag 都被视为判别属性。

brvekthn

brvekthn6#

以下是发生的情况:检查器类将 tagdata 都归类为潜在的判别属性。data 被认为是判别属性的原因是因为类型 boolean 是两个文字类型的并集(我们只需要一个文字类型或其并集的存在)。在发现多个候选判别属性时,我们决定完全不进行区分,并选择并集中的最后一个类型用于错误详细说明。我们可能应该只选择第一个候选判别属性,因为那样至少会报告一些更有意义的信息。

brccelvz

brccelvz7#

这个问题在5.1.6版本中已经修复。现在的错误是:

Type '{ tag: "a"; data: boolean; }' is not assignable to type 'DiscriminatedUnion'.
  Types of property 'data' are incompatible.
    Type 'boolean' is not assignable to type 'string'.

相关问题