Skip to content

Instantly share code, notes, and snippets.

@bradennapier
Last active January 19, 2023 10:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bradennapier/1dbc2933148b6a5917536863e34d8613 to your computer and use it in GitHub Desktop.
Save bradennapier/1dbc2933148b6a5917536863e34d8613 to your computer and use it in GitHub Desktop.
Typescript type resolution utility
/**
* Types declared in this file will be globally available throughout the project. These should
* be used sparsely and mainly for utility types (such as $Debug) that are likely unused outside
* of being helper utilities.
*
* @note
* If needing to `import` in this file, the global types must all be wrapped in
* `declare global` or they will no longer be seen as global.
*/
/**
* When running $Debug, these values will not be mapped into. One must be careful here as
* interfaces do not often match with `extends` in Typescript (aka O[K] extends React.CSSProperties won't
* work)
*/
type $DebugNoExtendValue<E> =
| null
| undefined
| void
| E
| ((...args: any[]) => any);
type $DebugDepth = 1 | 2 | 3 | 4 | 5;
type $DebugDepthDefault = 5;
/**
* $Debug<Type> allows you to expand out types in a way that will allow you to see
* exact properties of a type that will be the result of the final calculations
* such as One & Two & { three: string } --> { one: string, two: string, three: string }
*
* @example
* type PropsLink = CommonLinkProps & {
* href: string;
* target?: string | undefined;
* className?: string | undefined;
* style?: React.CSSProperties | undefined;
* } & {
* icon?: string | undefined;
* }
*
* type CheckProps = $Debug<PropsLink>
*
* type CheckProps = {
* color?: string | undefined;
* type?: "secondary" | "primary" | "dark" | "error" | "success" | "light" | undefined;
* children: React.ReactNode;
* href: string;
* target?: string | undefined;
* className?: string | undefined;
* style?: React.CSSProperties | undefined;
* icon?: string | undefined;
* }
*/
type $Debug<T, E = never> = T extends $DebugNoExtendValue<E>
? T
: T extends object
? T extends infer O
? {
[K in keyof O]: O[K];
}
: never
: T;
/**
* $DebugDeep<Type, Exclude, Depth> allows you to expand out types in a way that will allow you to see
* exact properties of a type that will be the result of the final calculations. Depth will default to the
* maximum levels (currently 5).
*
* You may exclude expansion of certain values by providing the `Exclude` option. If you want to define
* Depth and not Exclude just provide `never`.
* such as One & Two & { three: string } --> { one: string, two: string, three: string }
*
* @example
* $DebugDeep<One & Two, SomeType>; // Don't expand values of SomeType if encountered
* $DebugDeep<One & Two, never, 1>; // Expand one level into (One & Two)
* $DebugDeep<One & Two, never, 4>; // Expand two levels into (One & Two)
* $DebugDeep<One & Two>; // Expand maximum levels into (One & Two)
*/
type $DebugDeep<
T,
E = never,
L extends $DebugDepth = $DebugDepthDefault
> = T extends $DebugNoExtendValue<E>
? T
: T extends object
? T extends infer O
? {
[K in keyof O]: L extends 1
? $DebugDeep1<O[K], E>
: L extends 2
? $DebugDeep2<O[K], E>
: L extends 3
? $DebugDeep3<O[K], E>
: L extends 4
? $DebugDeep4<O[K], E>
: L extends 5
? $DebugDeep5<O[K], E>
: $DebugDeep<T, E, $DebugDepthDefault>;
}
: never
: T;
type $DebugDeep1<T, E = never> = T extends $DebugNoExtendValue<E>
? T
: T extends object
? T extends infer O
? {
[K in keyof O]: O[K];
}
: never
: T;
type $DebugDeep2<T, E = never> = T extends $DebugNoExtendValue<E>
? T
: T extends object
? T extends infer O
? {
[K in keyof O]: $DebugDeep1<O[K], E>;
}
: never
: T;
type $DebugDeep3<T, E = never> = T extends $DebugNoExtendValue<E>
? T
: T extends object
? T extends infer O
? {
[K in keyof O]: $DebugDeep2<O[K], E>;
}
: never
: T;
type $DebugDeep4<T, E = never> = T extends $DebugNoExtendValue<E>
? T
: T extends object
? T extends infer O
? {
[K in keyof O]: $DebugDeep3<O[K], E>;
}
: never
: T;
type $DebugDeep5<T, E = never> = T extends $DebugNoExtendValue<E>
? T
: T extends object
? T extends infer O
? {
[K in keyof O]: $DebugDeep4<O[K], E>;
}
: never
: T;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment