Skip to content

Instantly share code, notes, and snippets.

@webstrand
Last active May 12, 2019 19:44
Show Gist options
  • Select an option

  • Save webstrand/465e8867bbf06ab405506ea90e342505 to your computer and use it in GitHub Desktop.

Select an option

Save webstrand/465e8867bbf06ab405506ea90e342505 to your computer and use it in GitHub Desktop.
Utility types for manipulating and traversing lists.
// Find the first item of a list.
export type Head<List extends any[]> = List['length'] extends 0 ? never : List[0];
// Find the last item of a list.
export type Last<List extends any[]> = number extends List['length'] ? List[1e+309] : _Last<List>;
type _Last<
Tuple extends any[],
_I extends number = Tail<Tuple>['length']
> = Tuple extends [] ? never : _I extends keyof Tuple ? Tuple[_I] : never;
// Produce a list skipping the first item.
export type Tail<List extends any[]> = ((..._: List) => any) extends ((_: any, ...tail: infer U) => any) ? U : never;
// Produce a list skipping the last item.
export type Init<List extends any[]> = number extends List['length'] ? List : _Init<List>;
type _Init<
Tuple extends any[],
_Tail extends any[] = Tail<Tuple>
> = {
[I in keyof _Tail]: I extends keyof Tuple ? Tuple[I] : never;
};
// True when the list has a prefix that is a tuple.
export type HasTuplePrefix<List extends any[]> =
// Only lists having a ...T[] component have (number extends List['length'])
number extends List['length']
// If the list is equivalen to an array of its first element, then the
// list has no leading required elements.
? List extends List[0][] ? List[0][] extends List ? false : true : true
: true;
// "unshift" the type H onto the beginning of List.
export type Unshift<List extends any[], H> =
((_: H, ...args: List) => any) extends (...args: infer U) => any ? U : [];
// Extract the required elements from the list.
export type ExtractTuplePrefix<List extends any[]> =
// If the list is finite, avoid doing recursive work and return the list directly.
number extends List['length']
? _ExtractTuplePrefix<List>
: List;
// Use only on Lists with non-finite components.
type _ExtractTuplePrefix<
List extends any[],
C extends number[] = [],
_Return = {
0: C,
1: _ExtractTuplePrefix<Tail<List>, Unshift<C, 0>>
}[List extends List[0][] ? List[0][] extends List ? 0 : 1 : 1]
> = _Return extends any[] ? _Return : never;
// Find all the suffixes of a list
export type Suffixes<
List extends any[],
_Tail extends any[] = Tail<List>
> = {
0: List,
1: List | Suffixes<_Tail>,
2: List[1e309][]
}[
_Tail['length'] extends 0
? 0
: number extends List['length']
? List extends List[0][] ? List[0][] extends List ? 2 : 1 : 1
: 1
];
// Find all the prefixes of a tuple, including the tuple itself.
export type Prefixes<List extends any[]> =
number extends List['length']
? List extends List[0][]
? List[0][] extends List
? List
: _Prefixes<List, Suffixes<_ExtractTuplePrefix<List>>> | List
: _Prefixes<List, Suffixes<_ExtractTuplePrefix<List>>> | List
: _Prefixes<List>;
type _Prefixes<
Tuple extends any[],
_Suffixes = Suffixes<Tuple>
> = {
[I in keyof _Suffixes]: I extends keyof Tuple ? Tuple[I] : never;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment