Skip to content

Instantly share code, notes, and snippets.

@3fuyang
Last active November 10, 2023 15:45
Show Gist options
  • Save 3fuyang/fe7fe44b6d7a2d7996577ab9c6324adc to your computer and use it in GitHub Desktop.
Save 3fuyang/fe7fe44b6d7a2d7996577ab9c6324adc to your computer and use it in GitHub Desktop.
Type Challenges
/* 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