1

I'm tying to write a helper type that intersects all types in a tuple type but with one logic in mind:

The types that add the most restrictions "win" over the others. The final intersection must be the most-restricted type that can be assigned to every individual type of the tuple.

An example. Let's call that helper type Join. Here are some assertions :

Join<[string, any[]]> // string & any[]
Join<[string, number, boolean]> // never (string & number & boolean)
Join<[string, { z: boolean }]> // string & { z: boolean }
Join<[{ x: number }, { y: string }]> // { x: number } & { y: string }
Join<[{ x: number }, { y: string }, { z: boolean }]> // { x: number } & { y: string } & { z: boolean }
Join<[any, { y: string }]> // { y: string }
Join<[any, string]> // string
Join<[unknown, string]> // string
Join<[any, unknown, any, any]> // unknown
Join<[any, any, any]> // any

I have no idea where to start. Any idea ? Thank you.

hakre
  • 193,403
  • 52
  • 435
  • 836
ostrebler
  • 940
  • 9
  • 32

2 Answers2

1
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

type Elem = any;

type FilterAny<
  Arr extends ReadonlyArray<Elem>,
  Result extends ReadonlyArray<Elem> = []
  > = Arr extends []
  ? Result
  : Arr extends readonly [infer H, ...infer Tail]
  ? Tail extends ReadonlyArray<Elem>
  ? IsAny<H> extends true
  ? FilterAny<Tail, Result>
  : FilterAny<Tail, [...Result, H]>
  : never
  : never;

type Join<T extends any[]> =
  FilterAny<T>['length'] extends 0
  ? any
  : UnionToIntersection<FilterAny<T>[number]>

// credits goes to https://stackoverflow.com/questions/55541275/typescript-check-for-the-any-type
type IsAny<T> = 0 extends (1 & T) ? true : false;


type Result1 = Join<[string, any[]]> // string & any[]
type Result2 = Join<[string, number, boolean]> // never (string & number & boolean)
type Result3 = Join<[string, { z: boolean }]> // string & { z: boolean }
type Result4 = Join<[{ x: number }, { y: string }]> // { x: number } & { y: string }
type Result5 = Join<[{ x: number }, { y: string }, { z: boolean }]> // { x: number } & { y: string } & { z: boolean }
type Result6 = Join<[any, { y: string }]> // { y: string }
type Result7 = Join<[any, string]> // string
type Result8 = Join<[unknown, string]> // string
type Result9 = Join<[any, unknown, any, any]> // unknown
type Result10 = Join<[any, any, any]> // any

FilterAny iterates over the tuples recursively and removes all any from the tuple

Join logic:

  1. If FilterAny returns empty array - return any
  2. return intersection of union of all array elements

More examples with tuple manipulations you can find in my blog

Playground

0

I did some playing around, this will support all your cases expect for one, so you can maybe iterate on that:

type IfAny<Test, True, False> = 0 extends (1 & Test) ? True : False


type Join<Arr extends [...any[]]> =  Arr extends [infer A] ? A  : Arr extends [infer A, ...infer Rest]  ? 
IfAny<A, Join<Rest>, A & Join<Rest>> : any

The only one not working to your specifications would be

type Test = Join<[any, unknown, any, any]> // you want unknown and it is any
phry
  • 35,762
  • 5
  • 67
  • 81