Last active
November 10, 2023 15:45
-
-
Save 3fuyang/fe7fe44b6d7a2d7996577ab9c6324adc to your computer and use it in GitHub Desktop.
Type Challenges
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
/* CheatSheet: A common pattern and its three states */ | |
// Left, Right, Exist | |
type L<T extends any[]> = T extends [infer L, ... infer R] ? L : T | |
type R<T extends any[]> = T extends [infer L, ... infer R] ? R : T | |
type E<T extends any[]> = T extends [infer L, ... infer R] ? true : false | |
// 1, [2], true | |
type L1 = L<[1, 2]> | |
type R1 = R<[1, 2]> | |
type E1 = E<[1, 2]> | |
// 1, [], true | |
type L2 = L<[1]> | |
type R2 = R<[1]> | |
type E2 = E<[1]> | |
// [], [], false | |
type L3 = L<[]> | |
type R3 = R<[]> | |
type E3 = E<[]> | |
/* ReadOnly */ | |
type MyReadonly<T> = { | |
readonly [P in keyof T]: T[P] | |
} | |
/* Await */ | |
// Wrong. | |
type MyAwaitWrong<P> = P extends Promise<infer R> ? (R extends Promise<infer S> ? S : R) : never | |
// Correct, note that MyAwait is called **recursively** inside its definition, so the false branch returns P, not never. | |
type MyAwait<P> = P extends Promise<infer R> ? MyAwait<R> : P | |
/* Concat */ | |
type Concat<T extends any[], U extends any[]> = [...T, ...U] | |
/* Tuple to Object */ | |
type TupleToObject<T extends readonly any[]> = { | |
[P in T[number]]: P | |
} | |
/* First of Array */ | |
type First<T extends any[]> = T extends [infer R, ...any[]] ? R : never | |
/* Length of Tuple */ | |
type Length<T extends readonly any[]> = T['length'] | |
/* Exclude */ | |
// Wrong | |
type MyExcludeWrong<T, U> = T extends infer A | infer B ? (A extends U ? never : A) | MyExcludeWrong<B, U> : T | |
// Correct, tuples seem to be automatically iterated in conditional types. | |
type MyExclude<T, U> = T extends U ? never : T | |
/* If */ | |
type If<C, T, F> = C extends true ? T : (C extends false ? F : never) | |
/* Includes */ | |
// Wrong | |
type IncludesWrong<T extends readonly any[], U> = T extends Array<infer P> ? (U extends P ? true : false) : false | |
// Correct, the T[number] converts a Tuple([x, y, z]) to a Union(x | y | z). | |
import type { Equal } from '@type-challenges/utils' | |
type Includes<T, U> = T extends [infer L, ... infer R] ? Equal<L, U> extends true ? true : Includes<R, U> : false | |
/* Push */ | |
type Push<T extends any[], U> = [...T, U] | |
/* Unshift */ | |
type Unshift<T extends any[], U> = [U, ...T] | |
/* Shift */ | |
type Shift<T extends any[]> = T extends [infer L, ... infer R] ? R : T | |
/* Parameters */ | |
type Call<T extends any[]> = (...args: T) => any | |
type MyParameters<T> = T extends Call<infer P> ? P : never | |
/* Last */ | |
type Last<T extends any[]> = T extends [infer L, ... infer R] ? R extends [] ? L : Last<R> : T | |
/* Pop */ | |
type Pop<T extends any[]> = T extends [infer L, ... infer M, infer R] ? [L, ...M] : T | |
/* Promise.all */ | |
declare function PromiseAll <T extends any[]> (values: readonly [...T]): Promise<{ | |
[N in keyof T]: T[N] extends Promise<infer P> ? P : T[N] | |
}> | |
/* Flatten */ | |
type Flatten<T> = T extends [infer L, ... infer R] ? L extends any[] ? [...Flatten<L>, ...Flatten<R>] : [L, ...Flatten[R]] : T | |
/* AnyOf */ | |
// note the empty object literal `{}`, which is handled single. | |
// @typescript-eslint: don't use `{}` as a type. `{}` actually means "any non-nullish value". | |
type AnyOf<T extends readonly any[]> = | |
T extends [infer L, ... infer R] | |
? (L extends '' | [] | false | 0 | |
? AnyOf<R> | |
: (keyof L extends undefined[] | |
? AnyOf<R> | |
: true)) | |
: false | |
/* FlattenDepth */ | |
// use an auxiliary array as a counter | |
type FlattenOnce<T extends readonly any[]> = | |
T extends [infer L, ... infer R] | |
? (L extends any[] | |
? [... L, ... FlattenOnce<R>] | |
: [L, ... FlattenOnce<R>]) | |
: T | |
type FlattenDepth<T extends readonly any[], U extends number = 1, C extends any[] = []> = | |
FlattenOnce<T> extends T | |
? T | |
: (C['length'] extends U | |
? T | |
: FlattenDepth<FlattenOnce<T>, U, [... C, any]>) | |
/* Without */ | |
type ToTuple<U extends number[] | number> = | |
U extends number | |
? U | |
: U[keyof U] | |
type Without<T extends any[], U extends number | number[]> = | |
T extends [infer L, ... infer R] | |
? L extends ToTuple<U> | |
? Without<R, U> | |
: [L, ... Without<R, U>] | |
: T | |
/* Get Return Type */ | |
type MyReturnType<T> = T extends (...args: any[]) => infer T ? T : never | |
/* Tuple to Union */ | |
type TupleToUnion<T> = | |
T extends [infer L, ... infer R] | |
? | |
TupleToUnion<R> extends [] | |
? L | |
: (L | TupleToUnion<R>) | |
: T |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment