Last active
January 22, 2021 05:58
-
-
Save chrismilson/bfeedeb6db51fdef1d92dfe2dceb959e to your computer and use it in GitHub Desktop.
Typescript Tuple Definition
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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] |
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]>
Here is a playground.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was inspired by @lazytype's comment.
I just added some comments documenting the intuition behind the types, and the check for negatives.