Last active
September 25, 2021 18:56
-
-
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.
This file contains hidden or 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
| /** | |
| * 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); | |
| /*///////////////////////////////////////////////////////////////////////////*/} |
This file contains hidden or 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
| /** | |
| * 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