Description
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
.