TypeScript联合类型未排除。

4
我对联合类型和 Exclude 存在问题(playground 链接)。
type Foo = {
  data: string;
  otherData: number;
};
type Bar = Omit<Foo,'otherData'>
// type Bar = { data: string; }; // Even this is not working
type FooBar = Foo | Bar;

type ShouldBeFoo = Exclude<FooBar, Bar>; // Not working
// ShouldBeFoo = never

type ShouldBeBar = Exclude<FooBar, Foo>; // Somehow working
// ShouldBeBar = { data: string }

我是否遗漏了与联合类型和/或 Exclude 相关的内容?

我也尝试过使用 TS 4.4 和 4.2,结果相同。


注意:

我们使用类型保护发现了这个问题,您可以在此处 playgorund 查看。


我注意到TypeScript的类型系统虽然表达能力强,但是不够稳健,这是按设计的,我猜测你可能遇到了其中的一个边缘情况。 - Dai
3个回答

1

PickOmit这样的实用类型非常适用于具有属性的接口或类型,因为第二个参数看起来是K的keyof

由于你的类型是联合类型,TS编译器无法推断出正确的行为。你可以尝试这个方法。


type Foo = {
    data: string;
    otherData: number;
};

type Bar = Omit<Foo, 'otherData'>

type FooBar = Foo | Bar;

type Reduce<T, K> = T extends K ? K : never;

type ShouldBeFoo = Reduce<FooBar, Foo>;
type ShouldBeBar = Reduce<FooBar, Bar>;



其实,这就是“排除(Exclude)”的确切定义,只不过被重命名为“规约(Reduce)”而已 ^^ - phry
@phry 的 Exclude 是 T extends K ? never : T - raina77ow
是的,你的确给出了“ExcludeExact”的精确定义。不过我有一个问题,当我使用“Reduce”而非“Exclude”时,为什么它不起作用呢? - Amir Saleem
你把我搞糊涂了。明白了。 - Amir Saleem
FYI:你创建的Reduce助手已经在TS中实现为Extract - raina77ow
好的,这很有见地。谢谢@raina77ow - Amir Saleem

1

“Exclude”指的是与某一类型相“匹配”的排除,而不是相等。

你从FooBar中排除了Bar。此时BarBarFoo都相“匹配”,因此都被排除了。

你可以使用以下方法:

type ExcludeExact<T, U> = T extends U ? U extends T ? T : never : never

1

看起来真正的问题在于对TS中extends检查工作方式的误解。引用文档

SomeType extends OtherType ? TrueType : FalseType;

When the type on the left of the extends is assignable to the one on the right, then you’ll get the type in the first branch (the “true” branch); otherwise you’ll get the type in the latter branch (the “false” branch).

在这种情况下,SomeType不必是OtherType的显式扩展(在“class SomeType extends OtherType”意义上):它足以让SomeType可分配给OtherType,具有与OtherType相同类型的所有属性。作为副作用,当类型Y是Omit的结果时,X extends Y检查总是通过的!因为Omit只减少属性数量,但不会改变它们的类型或创建新的属性。
type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }

现在,由于Exclude泛型实际上看起来像这样:
type Exclude<T, U> = T extends U ? never : T

你的联合类型(Foo | Bar)通过extends Bar检查没有问题,因为联合中的两个部分都通过了检查。然而,Bar extends Foo检查失败,这就是为什么你的表达式对于shouldBeBar情况能正常工作的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接