Skip to content

Instantly share code, notes, and snippets.

@webstrand
Last active September 25, 2021 18:56
Show Gist options
  • Select an option

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

Select an option

Save webstrand/bac2bc752e3f6b22892a155ed1efa536 to your computer and use it in GitHub Desktop.
Definition of type alias Prefixes<T> which generates all of the prefixes for some tuple T. Including unit tests.
/**
* Get the union of all prefixes of some tuple. Finite, optional, and variadic tuples are
* all supported. Labels are only preserved for finite tuples without optional members.
*/
type Prefixes<T extends readonly unknown[]> =
T extends { length: infer X } & { length: infer Y }
? (X extends unknown ? Y extends X ? 0 : 1 : never) extends 0
? number extends T["length"] // If the tuple is variadic
? PrefixesNoLabels<T> // we bail out and use the variadic supporting type
: PrefixesFinite<T>
: PrefixesNoLabels<T> // otherwise we bail out and use the optional member supporting type
: never;
/** @private
* Get the union of all prefixes of some tuple. Only finite tuples are supported.
*/
type PrefixesFinite<T extends readonly unknown[]> =
T extends readonly [...infer U, any] // Remove one element at a time
? | [...T] // wrapped to erase any readonly attributes
| PrefixesFinite<U>
: never;
/** @private
* Get the union of all prefixes of some tuple. Finite and variadic tuples are
* both supported. But labels are never preserved.
*/
type PrefixesNoLabels<T extends readonly unknown[], Acc extends readonly unknown[] = [T extends [infer U, ...any] ? U : never]> =
T extends readonly [...Acc, infer U, ...any]
? Acc | PrefixesNoLabels<T, [...Acc, U]>
: T;
/*///////////////////////////////////////////////////*/namespace CheckPrefixes {
// @ts-expect-error
if(1 as 0) return;
eval(`throw "non-executable code"`);
// Tuples without guaranteed elements
Exact<Prefixes<[]>, never>(true);
Exact<Prefixes<string[]>, string[]>(true);
// Finite tuples of various lengths
Exact<Prefixes<[1]>, [1]>(true);
Exact<Prefixes<[1,2]>, [1] | [1,2]>(true);
Exact<Prefixes<[1,2,3]>, [1] | [1,2] | [1,2,3]>(true);
Exact<Prefixes<[1,2,3,4]>, [1] | [1,2] | [1,2,3] | [1,2,3,4]>(true);
// Unions of finite tuples
Exact<Prefixes<[1] | [1,2,3,4]>, [1] | [1,2] | [1,2,3] | [1,2,3,4]>(true);
Exact<Prefixes<[1,2] | [3,4,5]>, [1] | [1,2] | [3] | [3,4] | [3,4,5]>(true);
Exact<Prefixes<[1,3] | [2,3]>, [1] | [1,3] | [2] | [2,3]>(true);
// Indefinite tuples of various lengths
Exact<Prefixes<[1,...string[]]>, [1,...string[]]>(true);
Exact<Prefixes<[1,2,...string[]]>, [1] | [1,2,...string[]]>(true);
Exact<Prefixes<[1,2,3,...string[]]>, [1] | [1,2] | [1,2,3,...string[]]>(true);
Exact<Prefixes<[1,2,3,4,...string[]]>, [1] | [1,2] | [1,2,3] | [1,2,3,4,...string[]]>(true);
// Optional tuples of various lengths
Exact<Prefixes<[1]>, [1]>(true);
Exact<Prefixes<[1,2?]>, [1,2?]>(true);
Exact<Prefixes<[1,2?,3?]>, [1,2?,3?]>(true);
Exact<Prefixes<[1,2,3?,4?]>, [1] | [1,2,3?,4?]>(true);
// Unions of indefinite tuples of various lengths
Exact<Prefixes<[1,...string[]] | [1,2,3,4,...string[]]>, [1,...string[]] | [1] | [1,2] | [1,2,3] | [1,2,3,4,...string[]]>(true);
Exact<Prefixes<[1,2,...string[]] | [3,4,5,6?,...string[]]>, [1] | [1,2,...string[]] | [3] | [3,4] | [3,4,5,6?,...string[]]>(true);
Exact<Prefixes<[1,3,...string[]] | [2,3,...string[]]>, [1] | [1,3,...string[]] | [2] | [2,3,...string[]]>(true);
Exact<Prefixes<[1,2,...string[]] | [1,2,...number[]]>, [1] | [1,2,...string[]] | [1,2,...number[]]>(true);
// Unions of finite and indefinite tuples
Exact<Prefixes<[1,2,3] | [1,2,...string[]]>, [1] | [1,2] | [1,2,...string[]] | [1,2,3]>(true);
Exact<Prefixes<[1,...string[]] | number[]>, [1,...string[]] | number[]>(true);
// Anomalous types
Exact<Prefixes<never>, never>(true);
Exact<Prefixes<readonly [1,2,3]>, [1] | [1,2] | [1,2,3]>(true);
/*///////////////////////////////////////////////////////////////////////////*/}
/**
* Example use of `Prefixes`. Bind does not work with indefinite length tuples
* when `args` has consumed more than the definite members of the tuple.
*/
function bind<
Args extends readonly any[],
Spec extends Spectator<(...args: Args) => unknown>,
As extends Prefixes<Args> | []
>(
thisArg: unknown,
audience: Audience.Iterable<Spec>,
...args: As
): (
...args: SpectatorParameters<Spec> extends [...As, ...infer C]
? C
: never
) => Map<Spec, SpectatorReturnType<Spec>>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment