假设我具有以下已区分的联合和一些关联的类型
type Union = 'a' | 'b';
type Product<A extends Union, B> = { f1: A, f2: B};
type ProductUnion = Product<'a', 0> | Product<'b', 1>;
现在我可以使用映射类型和Exclude
type UnionComplement = {
[K in Union]: Exclude<Union, K>
};
// {a: "b"; b: "a"}
type UnionComplementComplement = {
[K in Union]: Exclude<Union, Exclude<Union, K>>
};
// {a: "a"; b: "b"}
到目前为止,所有这些都是有意义的,但是当我尝试采用双补码时,ProductUnion
的情况就破裂了。第一个补码效果很好
type ProductComplement = {
[K in Union]: Exclude<ProductUnion, { f1: K }>
};
// {a: Product<'b', 1>; b: Product<'a', 0>}
无论我怎样尝试,双补码都是错误的
type ProductComplementComplement = {
[K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, { f1: K }>>
};
// {a: ProductUnion; b: ProductUnion}
我不知道错误在哪里,因为如果我替换类型,那么它将起作用。进行双补码时,K
只有2个值,因此让我们尝试第一个值
type First = Exclude<ProductUnion, Exclude<ProductUnion, { f1: 'a' }>>;
// {f1: 'a'; f2: 0}
第二个也可以
type Second = Exclude<ProductUnion, Exclude<ProductUnion, { f1: 'b' }>>;
// {f1: 'b'; f2: 1}
所有组成部分都起作用,但是当以映射类型组合时,它似乎崩溃了。我在这里想念什么?
一时兴起,我尝试添加一个类型参数,以了解通过抽象补码过程会发生什么
type Complementor<T> = {
[K in Union]: Exclude<T, { f1: K }>
};
type DoubleComplementor<T> = {
[K in Union]: Exclude<T, Exclude<T, { f1: K }>>
};
现在,如果我将参数化类型应用于ProductUnion
,它将完全按照我的预期工作
type Complement = Complementor<ProductUnion>;
// {a: Product<'b', 1>; b: Product<'a', 0>}
type DoubleComplement = DoubleComplementor<ProductUnion>;
// {a: Product<'a', 0>; b: Product<'b', 0>}
答案 0 :(得分:2)
这确实是一个错误:https://github.com/Microsoft/TypeScript/issues/28824。多亏了安德斯(Anders)和团队,下一个版本应该具有更一致的行为。
答案 1 :(得分:0)
抽象类型别名的行为不应与内联别名完全相同。我认为这就是重点。
编辑:
好的,它看起来确实像个错误。
type E1 = Exclude<{ f1: 'a' } | { f1: 'b' },
Exclude<{ f1: 'a' } | { f1: 'b' }, { f1: 'a' }>>;
// E1 = { f1: "a" }
type E2<K> = Exclude<{ f1: 'a' } | { f1: 'b' },
Exclude<{ f1: 'a' } | { f1: 'b' }, { f1: K }>>;
// E2<K> = { f1: "a" } | { f1: "b" }
// ^ no `K` in the resulting type
// the compiler has somehow eliminated `K` from the resulting type
// no matter what you do from here on, doesn't get re-evaluated with K in it.
//
type E2a = E2<'a'>;
// E2a = { f1: "a" } | { f1: "b" }
type E2b = E2<'b'>;
// E2b = { f1: "a" } | { f1: "b" }