TypeScript 当联合类型的判别符的一个组成部分时,不显示判别特定错误消息,

o3imoua4  于 6个月前  发布在  TypeScript
关注(0)|答案(3)|浏览(72)

TypeScript版本:3.9.0-dev.20200319
搜索词:discrimant tagged union assignable
代码
一个简化的复现示例,基于实际代码:

interface A { x: number }
interface B { x: string }

interface U$A { kind: 'A', body: A }
interface U$B { kind: 'B', body: B }
interface U$null { kind: 'C' | 'D', body?: undefined }

type U = U$A | U$B | U$null;

declare function foo(u: U): void;

foo({kind: 'A', body: { x: 42 }});  // OK
foo({kind: 'B', body: { x: 42 }});  // Not OK

预期行为:
第二个 x: 42 得到一个波浪线和错误 Type 'number' is not assignable to type 'string'.
实际行为:
第二个 foo 调用的整个参数得到一个波浪线和错误:
参数类型为 '{ kind: "B"; body: { x: number; }; }' 的错误,无法分配给 U 类型的参数。类型 '{ kind: "B"; body: { x: number; }; }' 无法分配给 U$null 类型。属性 'kind' 的类型不兼容。类型 '"B"' 无法分配给类型 '"C" | "D"'。
我希望通过检查 kind 属性首先缩小参数的类型为 U$B。目前,当涉及的类型的形状变大时,开发者用户体验并不理想,而且很难弄清楚哪个属性出错(在我的实际情况中,联合有250+个成员,错误信息难以理解)。
注意事项
如果将 interface B 中的 x 更改为 y,错误消息会出现得很好。
此外,如果我注解掉对 U$null 的两个引用,我会得到一个不同的(但也是可接受的)错误。
Playground链接:https://www.typescriptlang.org/v2/en/play?ts=next#code/JYOwLgpgTgZghgYwgAgILIN7IB4C5kgCuAtgEbTIC+AUKJLIigEKY74DOYUoA5ldbXDR4SZAFUAJOiwBrUABN8AclRKANMlIB7eQE986GnWGNxElrIXKm6zTv3IWRoQ1GSiAGw+s5IRciUAYSVkAB8AgBFbbT0AfnxCPwgYUAh5fmowXQAHFDFkAF4zdHDJFlKJTw8AbgF5CAQPOCgUGESEMGAtEGQYLS0ACkJ8MQBKfAA3LWB5Wuo+wYxffxVo+3wsPGQAFgAmKkpR2oWBpasAmw0Yh038PYOj6iA
相关问题
似乎没有与此完全相同的问题(凭直觉猜测)。

uhry853o

uhry853o1#

提供更好的错误信息的逻辑取决于在目标类型中找到一个具体的判别标准,我们不认为联合是一个有效的判别标准——换句话说,U$null 是在这里出问题的地方,如果用两种不同类型的扩展形式写出来会更好。

wwwo4jvm

wwwo4jvm2#

感谢RyanCavanaugh的建议,我尝试了扩展U$null,但仍然发现判别式没有先应用:
参数类型为'{ kind: "B"; body: { x: number; }; }'的参数不能分配给类型为'U'的参数。类型'{ kind: "B"; body: { x: number; }; }'不能分配给类型'U$D'。属性'kind'的类型不兼容。类型'"B"'不能分配给类型'"D"'。
Playground链接

ldioqlga

ldioqlga3#

我在使用useAsyncFn from react-use时遇到了这个问题:
Playground链接

export declare type AsyncState<T> = {
    loading: boolean;
    error?: undefined;
    value?: undefined;
} | {
    loading: true;
    error?: Error | undefined;
    value?: T;
} | {
    loading: false;
    error: Error;
    value?: undefined;
} | {
    loading: false;
    error?: undefined;
    value: T;
};

function foo(x: AsyncState<readonly number[]>){
    let y: AsyncState<number[]> = x;
}

错误信息:

Type 'AsyncState<readonly number[]>' is not assignable to type 'AsyncState<number[]>'.
  Type '{ loading: true; error?: Error | undefined; value?: readonly number[] | undefined; }' is not assignable to type 'AsyncState<number[]>'.
    Type '{ loading: true; error?: Error | undefined; value?: readonly number[] | undefined; }' is not assignable to type '{ loading: false; error?: undefined; value: number[]; }'.
      Types of property 'loading' are incompatible.
        Type 'true' is not assignable to type 'false'.

预期的错误信息应该是类似于:

The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.

相关问题