取受歧视工会的补充
Taking complements of discriminated unions
假设我有以下可区分的联合和一些关联类型
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>}
抽象类型别名的行为方式不应与内联别名完全相同。我认为这就是重点。
编辑:
好的,它看起来确实像一个错误。
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" }
这确实是一个错误:https://github.com/Microsoft/TypeScript/issues/28824。感谢 Anders 和团队,下一个版本应该有更一致的行为。
假设我有以下可区分的联合和一些关联类型
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>}
抽象类型别名的行为方式不应与内联别名完全相同。我认为这就是重点。
编辑:
好的,它看起来确实像一个错误。
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" }
这确实是一个错误:https://github.com/Microsoft/TypeScript/issues/28824。感谢 Anders 和团队,下一个版本应该有更一致的行为。