Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Equivalence between tuple containing union type and union type of tuples #39357

Closed
ruizb opened this issue Jul 1, 2020 · 4 comments · Fixed by #39393
Closed

Equivalence between tuple containing union type and union type of tuples #39357

ruizb opened this issue Jul 1, 2020 · 4 comments · Fixed by #39393
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@ruizb
Copy link

ruizb commented Jul 1, 2020

Search Terms

tuple, union type, equivalence, distributivity, infer, 2322

Suggestion

I'd like TypeScript to make this possible:

  • [any, 'a' | 'b'] equivalent as [any, 'a'] | [any, 'b']

Use Cases

I'm writing a union type of tuples, which are containing values which types are also union types.

The thing is, some of these values are mutually exclusive, so I can't write a single tuple ['a' | 'b', 'c' | 'd'], since I don't want the ['a', 'd'] pair to exist (for example).

I have to manually write the possible pairs, e.g. ['a', 'c'], ['b', 'c'] and ['b', 'd'], which is fine. However, when building a value for this type, by making some checks with conditional branching to make sure the first element is either 'a' or 'b', the type guard applied doesn't allow me to build a value of this type.

This seems to work well with objects (so it's a workaround I guess?), but I'd like to avoid the struggle of finding names - let's be honest, the hardest part of our job - for each property. Since objects and tuples are isomorphic, tuples should behave the same and not throw an error (cf. example).

I'm assuming such feature could have a significant impact regarding TSC performance: if our tuples contain union types with tens of possible values, then finding all the possible tuples for all these union types could be quite heavy.

Example

TypeScript version: 3.9.2.

type A = [number, 'yes'] | [number, 'no'] | [string, 'maybe']
type B = 'yes' | 'no' | 'maybe'

declare const b: B
const a: A = (b === 'yes' || b === 'no') ? [12, b] : ['12', 'maybe']

Here we have a TS2322 error on a:

Type '[number, "yes" | "no"] | [string, "maybe"]' is not assignable to type 'A'.
  Type '[number, "yes" | "no"]' is not assignable to type 'A'.
    Type '[number, "yes" | "no"]' is not assignable to type '[number, "yes"]'.
      Type '"yes" | "no"' is not assignable to type '"yes"'.
        Type '"no"' is not assignable to type '"yes"'.

However, when doing the same with an object:

type C = { value: number, tag: 'yes' } | { value: number, tag: 'no' } | { value: string, tag: 'maybe' }

const c: C = (b === 'yes' || b === 'no') ? { value: 12, tag: b } : { value: '12', tag: 'maybe' }

There's no error on c, which is the behavior I'm looking for, but for a.

(Playground)

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jul 1, 2020

I guess the same logic could be applied from #30779, which is only done for discriminated unions. Honestly, I'm not sure why we don't apply the same checks, since internally they're both just object types.

Any thoughts @rbuckton?

@DanielRosenwasser DanielRosenwasser added this to Agenda in Design Meeting Docket via automation Jul 1, 2020
@rbuckton
Copy link
Member

rbuckton commented Jul 2, 2020

This seems to be failing because of logic added to propertiesRelatedTo specific to tuple types that ignores the excludedProperties set that is passed in when comparing to a discriminated union.

@rbuckton
Copy link
Member

rbuckton commented Jul 2, 2020

I would say that this is a bug as this is intended to work. Fixing this looks like it will require a simple one-line change in propertiesRelatedTo and some additional work in indexTypesRelatedTo to exclude the types of the excluded properties from comparison.

@ruizb
Copy link
Author

ruizb commented Jul 3, 2020

Hello there, I didn't realize it was actually a bug, I'm glad you found a fix so quickly! Thank you ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
Development

Successfully merging a pull request may close this issue.

3 participants