Skip to content

Instantly share code, notes, and snippets.

@Alia5
Last active May 17, 2023 08:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Alia5/bc7f1dd772c76736c990ed6993996f22 to your computer and use it in GitHub Desktop.
Save Alia5/bc7f1dd772c76736c990ed6993996f22 to your computer and use it in GitHub Desktop.
Advanced Typescript Cheatsheet
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, '.'>>;
@Alia5
Copy link
Author

Alia5 commented Jan 2, 2023

@boronine
Copy link

I ran into some strange behaviour where ValidPaths treated const 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 of ValidPaths that doesn't suffer from this problem.

export type ValidPaths<T> = T extends string | number | boolean | null | undefined
  ? never
  : {
      [K in keyof T]: K extends string ? `${K}.${ValidPaths<T[K]>}` | K : never;
    }[keyof T];

@Alia5
Copy link
Author

Alia5 commented May 17, 2023

🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment