Skip to content

Instantly share code, notes, and snippets.

@chrismilson
Last active January 22, 2021 05:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrismilson/bfeedeb6db51fdef1d92dfe2dceb959e to your computer and use it in GitHub Desktop.
Save chrismilson/bfeedeb6db51fdef1d92dfe2dceb959e to your computer and use it in GitHub Desktop.
Typescript Tuple Definition
/**
* Constructs tuples of `never` of length 2^K for all 0 ≤ K and 2^(K - 1) ≤ N.
* The tuples are in decreasing order by their lengths.
*
* The placeholder type `never` is chosen because it lets us test the length, as
* opposed to a generic type, which may be extended by undefined, and give us
* unexpected results.
*
* This type should not be used directly; it is a support type for Tuple.
*
* @param N The number N
* @param R A tuple of the tuples constructed so far.
*/
type _BuildLargeTuples<
N extends number,
R extends never[][]
> = R[0][N] extends never ? R : _BuildLargeTuples<N, [[...R[0], ...R[0]], ...R]>
/**
* Concatenates the large tuples from _BuildLargeTuples while not exceeding the
* specified length in order to build a tuple of length exactly N.
*
* This type should not be used directly; it is a support type for Tuple.
*/
type _ConcatUntilDone<
N extends number,
R extends never[],
L extends never[][]
> = R['length'] extends N
? R
: _ConcatUntilDone<
N,
[...L[0], ...R][N] extends never ? R : [...L[0], ...R],
L extends [L[0], ...infer U] ? (U extends never[][] ? U : never) : never
>
/**
* Replaces the never placeholder type in our tuples with the intended type for
* a tuple.
*/
type _ReplaceNever<R extends never[], T> = { [P in keyof R]: T }
/**
* A generic tuple type.
*/
// The `number extends N` check makes sure that N is not the generic number
// type. The `[][K] extends never` check makes sure that K is not negative.
type Tuple<T, N extends number> = number extends N
? T[]
: {
[K in N]: [][K] extends never
? never
: _BuildLargeTuples<K, [[never]]> extends infer U
? U extends never[][]
? _ReplaceNever<_ConcatUntilDone<K, [], U>, T>
: never
: never
}[N]
@chrismilson
Copy link
Author

This was inspired by @lazytype's comment.

I just added some comments documenting the intuition behind the types, and the check for negatives.

@chrismilson
Copy link
Author

chrismilson commented Jan 22, 2021

The proposal in the pull request recurses to a depth that is linear in N. This approach however recurses to a depth that is O(log n), allowing much longer tuples.

Tuple type Implementation proposal
type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>

@chrismilson
Copy link
Author

Here is a playground.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment