Last active
October 25, 2024 07:19
-
-
Save Alia5/bc7f1dd772c76736c990ed6993996f22 to your computer and use it in GitHub Desktop.
Advanced Typescript Cheatsheet
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
export type Await<T> = T extends PromiseLike<infer U> ? Await<U> : T; | |
export type IsPromise<T> = PromiseLike<infer U> ? true : false; | |
export type Length<T> = T extends { length: infer L } ? L : never; | |
export type KeysOfType<O, T> = { | |
[K in keyof O]: O[K] extends T ? K : never; | |
}[keyof O]; | |
// ConvertLiterals would convert literal types like `1337` to their base type like `number` if set to true | |
export type PickByType<O, T, ConvertLiterals extends boolean = false> = { | |
[K in KeysOfType<O, T>]: ConvertLiterals extends true ? T : O[K] | |
}; | |
export type OneOf<T, Strict extends boolean = true> = { | |
[OuterKey in keyof T]: Strict extends false | |
? { [K in OuterKey]: T[K] } | |
: { [InnerKey in OuterKey|keyof T]?: InnerKey extends OuterKey ? T[OuterKey] : never } & { [TheKey in OuterKey]: T[OuterKey] } | |
}[keyof T]; | |
type Push<T extends unknown[], U> = T extends [...infer R] ? [...T, U] : never; | |
type PushFront<T extends unknown[], U> = T extends [...infer R] ? [U, ...T] : never; | |
type Pop<T extends unknown[]> = T extends [...infer R, infer U] ? U : never; | |
type PopFront<T extends unknown[]> = T extends [infer U, ...infer R] ? U : never; | |
type Shift<T extends unknown[]> = T extends [infer U, ...infer R] ? R : never; | |
type ShiftRight<T extends unknown[]> = T extends [...infer R, infer U] ? R : never; | |
type Reverse<T extends unknown[], U extends unknown[] = []> = Length<T> extends 1 ? Push<U, Pop<T>> : Reverse<ShiftRight<T>, Push<U, Pop<T>>>; | |
type Filter<T extends unknown[], U> = T extends [] ? [] : T extends [infer F, ...infer R] ? F extends U ? Filter<R, U> : [F, ...Filter<R, U>] : never | |
type TupleIncludes<T extends unknown[], U> = Length<Filter<T, U>> extends Length<T> ? false : true | |
type StringIncludes<S extends string, D extends string> = S extends `${infer T}${D}${infer U}` ? true : false; | |
type Includes<T extends unknown[]|string, U> = T extends unknown[] ? TupleIncludes<T, U> : T extends string ? U extends string ? StringIncludes<T, U> : never : never; | |
type Split<S extends string, D extends string> = | |
string extends S ? string[] : | |
S extends '' ? [] : | |
S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S]; | |
type Join<T extends unknown[], D extends string> = string[] extends T ? string : T extends string[] | |
? PopFront<T> extends string ? Length<T> extends 1 ? `${PopFront<T>}` : `${PopFront<T>}${D}${Join<Shift<T>, D>}` : never | |
: never; | |
type ValidPaths<T> = keyof T extends never ? never : ({ | |
[K in keyof T]: T[K] extends never ? never : T[K] extends Record<string|number|symbol, unknown> | |
? K extends string ? `${K}.${ValidPaths<T[K]>}` | K : never | |
: K | |
})[keyof T] & string; | |
type ValidPathTuples<T> = keyof T extends never ? never : ({ | |
[K in keyof T]: T[K] extends never ? never : T[K] extends Record<string|number|symbol, unknown> | |
? [K, ...ValidPathTuples<T[K]>] | [K] | |
: [K] | |
})[keyof T]; | |
type NestedType<T, P extends string> = ( | |
Includes<P, '.'> extends true | |
? PopFront<Split<P, '.'>> extends keyof T | |
? NestedType<T[PopFront<Split<P, '.'>>], Join<Shift<Split<P, '.'>>, '.'>> | |
: never | |
: P extends keyof T ? T[P] : never | |
); | |
type NestedTypeByTuple<T, P extends string[]> = ( | |
Length<P> extends 1 | |
? Pop<P> extends keyof T ? T[Pop<P>] : never | |
: PopFront<P> extends keyof T ? Shift<P> extends string[] | |
? NestedTypeByTuple<T[PopFront<P>], Shift<P>> | |
: never : never | |
); | |
type NestedTypeUsingTuplesAgain<T, P extends ValidPaths<T>> = NestedTypeByTuple<T, Split<P, '.'>>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I ran into some strange behaviour where
ValidPaths
treatedconst enum
as an object and iterated over its properties (charCodeAt
etc.). Getting to the bottom of this issue is above my pay grade but I was able to create an alternative version ofValidPaths
that doesn't suffer from this problem.