Last active
January 19, 2023 10:42
-
-
Save bradennapier/1dbc2933148b6a5917536863e34d8613 to your computer and use it in GitHub Desktop.
Typescript type resolution utility
This file contains hidden or 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
/** | |
* 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