Typescript: TS 3.7rc regression on intersected union types

Created on 28 Oct 2019  路  9Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.6.3 VS 3.7-beta/nightly (from playground)

Search Terms: discriminated union, intersection type, tag in intersection, extract by tag

Code

We tried the new TS 3.7rc in our project and encountered a problem with our generated types. The types are generated by GraphQLCodegen.
Our code compiles without errors or warning in TS 3.6.3 but has compile errors in 3.7rc and in the nightly build of the playground.
We reduced the code to get the following situation in the playground:

http://www.typescriptlang.org/play/?ts=3.7-Beta#code/DYUwLgBAngXBCiAPMAnAhgYzAHgEoHtQAaCAbwgH0KwoAHEAOzQFsQB+OAclxAHMBLfAwKhOEAL4A+CAF4IDAK7BgENAGdVDKAFgAUKEgAvWdAB0KPoIZ6bugPR2IPNZH4bejEOjAgAJhAAzFHxmCABxdFoACwBFYD0aeidLIRNSPQhKajpGFnY4ACIeASECgG4MiH5fOBcUfgZeCt1MplYOCDqGpr1xZsSQZJLhQkG5dJasgbb8iCKUkdByyura1G7mzIthuGKrZr6EnIgAQV9mBpExsimcmY6Cs4vFkHKqms71xolmo6Sr2SVAA+EAAFOQqNM8g8npdRgUJBAAGQQAAK-AwAGtsLCXiQCtUCpIAJTAsEQ7L0e6FPZCK4I8TE5FojHY2l4ubbKxE366IA

The error seems to occur, when I try to use the Extract type on a field that is added in an intersection type.

Expected behavior:
No errors in TS 3.7

Bug Fix Available

Most helpful comment

Seems like a pretty serious bug. Should it really be part of 3.8, and not 3.7?

I use intersection types and optional properties together very often. This bug is bad enough that I can't upgrade to TS 3.7

All 9 comments

Minimal repro:

type U = { kind?: 'A', a: string } | { kind?: 'B' } & { b: string };
declare let x: Extract<U, { kind?: 'A' }>
x.a;

Notable criteria:

  1. If kind is not optional, no error
  2. If the non-matching union constituent is not an intersection, no error

While instantiating this usage of Extract, the checker basically comes to this:

type Source = { kind?: 'B' } & { b: string };
type Target = { kind?: 'A' }
// Source is assignable to Target if any constituents of Source are.
// 1. Is `{ kind?: 'B' }` assignable to `{ kind?: 'A' }`? Nope.
// 2. Is `{ b : string }` assignable to `{ kind?: 'B' }`?
// Actually yes, because `kind` is optional in Target, and we don鈥檛 care that
// these have no overlapping members because we鈥檙e comparing an intersection
// constituent鈥攖hat check will be performed at the top level.

Not sure yet how this was avoided previously / what introduced this regression, because the fix wasn鈥檛 obvious while discovering the problem. Going back to 3.6 to see what happened there.

Seems like a pretty serious bug. Should it really be part of 3.8, and not 3.7?

I use intersection types and optional properties together very often. This bug is bad enough that I can't upgrade to TS 3.7

I'd guess that every user of GraphQL codegen should be affected when they are using Extract in the __typename as this is generated in ts as an optional property.

@andreasgruenh as a workaround, could graphql stop generating an optional discriminant?

Yes, that's true, you can add nonOptionalTypename: true to the codegen.yml
For new projects this might be a solution. When using this flag in our codebase, 110 new errors appear, because various places assume the typename to be optional :/

I think I found a workaround by writing a special mapped type ExtractByOptionalTag which can be used for our typename usecase.

Code:

type U = ({ kind?: "A" } & { a: string }) | ({ kind?: "B" } & { b: string });
declare let x: ExtractByOptionalTag<"kind", U, "A">;
x.a

type SingleRequired<T extends Object, TKey extends keyof T> = T &
    Required<Pick<T, TKey>>;

type SingleOptional<T extends Object, TKey extends keyof T> = Omit<T, TKey> &
    Partial<Pick<T, TKey>>;

type ExtractByOptionalTag<
    TTagKey extends string,
    TObject extends { [key in TTagKey]?: string },
    TTagValue extends TObject[TTagKey]
    > = SingleOptional<
        Extract<SingleRequired<TObject, TTagKey>, { [key in TTagKey]: TTagValue }>,
        TTagKey
    >;

Playground link: http://www.typescriptlang.org/play/?ts=3.7-Beta#code/C4TwDgpgBAqlC8UAUBvKBrAlgOwCYH4AuKAIgEESoBfKAMijQENiBnYAJxwHNqBKKAD7I0WPEVIAhSjXpoARqw7c+AbgCwAKFwQAxgBtG7aHojAoAD2IBRcx0Y7gEkAHkwwTAHtsjPQBVGXAA8JKK4JAA0sJHkJAB86hrmAHSMmpqgkFAAytwmAEoQAI4ArphGuIG+UBC2EHgsUM5yAFa6wJG+ANIQINW19Rg9HgBmUL6xCGN0mlCzUAUlZRAVAAqYOuiVHd0gsfFpGhnQOdhcJq7uXj6VfcB1uA1NrQ7bPbf3DehDo+OTzgC2mGAWzGOwmtBmcxWhnc1zWGxBXR6ewS6XA0BsdgcTgunm8fgCgUhs18-i4O3eAzYnFO4WJYyebUpDwYUAA2l9ejgxmSdgBdcTU5RUOkaOY8gIANR8xWgNTuA18jIcbNJAX59ImiBOZwguKueiJYvFc0x7HswJ1+SKpXKlWV7Ql5ORkTQHLe3LVzpAfOIXulell1FiopNcy9O01CSAA

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Antony-Jones picture Antony-Jones  路  3Comments

weswigham picture weswigham  路  3Comments

remojansen picture remojansen  路  3Comments

siddjain picture siddjain  路  3Comments

jbondc picture jbondc  路  3Comments