Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Nested Exclude has unexpected behavior #28824

Copy link
Copy link
@ghost

Description

TypeScript Version:

$ tsc --version
Version 3.3.0-dev.20181122

Search Terms:
unexpected, exclude
Code

/*
  Some basic definitions of unions
*/
type Union = 'a' | 'b';
type Product<A extends Union, B> = { f1: A, f2: B};
type ProductUnion = Product<'a', 0> | Product<'b', 1>;

/*
  These work as I would expect. Each element in the union is mapped
  the complement or the double complement with nested 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"}

/*
  This also works as I would expect
*/
type ProductComplement = {
  [K in Union]: Exclude<ProductUnion, { f1: K }>
};
// {a: Product<'b', 1>; b: Product<'a', 0>}

/*
  Double complement on the other hand doesn't work
*/
type ProductComplementComplement = {
  [K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, { f1: K }>>
};
// {a: ProductUnion; b: ProductUnion}

/*
  Explicit inlining works as I would expect
*/
type First = Exclude<ProductUnion, Exclude<ProductUnion, { f1: 'a' }>>;
// {f1: 'a'; f2: 0}
type Second = Exclude<ProductUnion, Exclude<ProductUnion, { f1: 'b' }>>;
// {f1: 'b'; f2: 1}

/*
  Making parametrized types works as I would expect
*/
type Complementor<T> = {
    [K in Union]: Exclude<T, { f1: K }>
};
type DoubleComplementor<T> = {
    [K in Union]: Exclude<T, Exclude<T, { f1: K }>>
};
type Complement = Complementor<ProductUnion>;
// {a: Product<'b', 1>; b: Product<'a', 0>}
type DoubleComplement = DoubleComplementor<ProductUnion>;
// {a: Product<'a', 0>; b: Product<'b', 0>}

Expected behavior:
I would expect the parametrized and non-parametrized type to work the same way but that's not
the case.

Actual behavior:
See the comments.

Playground Link: > }; // {a: "a"; b: "b"} /* This also works as I would expect */ type ProductComplement = { [K in Union]: Exclude }; // {a: Product<'b', 1>; b: Product<'a', 0>} /* Double complement on the other hand doesn't work */ type ProductComplementComplement = { [K in Union]: Exclude> }; // {a: ProductUnion; b: ProductUnion} /* Explicit inlining works as I would expect */ type First = Exclude>; // {f1: 'a'; f2: 0} type Second = Exclude>; // {f1: 'b'; f2: 1} /* Making parametrized types works as I would expect */ type Complementor = { [K in Union]: Exclude }; type DoubleComplementor = { [K in Union]: Exclude> }; type Complement = Complementor; // {a: Product<'b', 1>; b: Product<'a', 0>} type DoubleComplement = DoubleComplementor; // {a: Product<'a', 0>; b: Product<'b', 0>}" rel="nofollow">link

Related Issues: Some issues related to Pick and Exclude but didn't seem to apply.

Reactions are currently unavailable

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptA bug in TypeScriptDomain: Conditional TypesThe issue relates to conditional typesThe issue relates to conditional typesFixedA PR has been merged for this issueA PR has been merged for this issue

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.