Last active
March 14, 2024 18:28
-
-
Save emileber/1efac6ba2d2801ce5963234ed0f3208f to your computer and use it in GitHub Desktop.
TypeScript helpers
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
/** | |
* @see https://www.typescriptlang.org/play?#code/C4TwDgpgBAkgZgUQI4FcCGAbAzgHgIIA0UAQkQCoBOKERAYplhAHxQC8UAFDmUxwJRsWZKBAAewCADsAJlih4AUFCgB+KAEYlUAFxQATALESZc7rwGshI8VNklVGnfq1rK1LbvrYIAbgULQSCgAdQoAS2A0ACMMCABpCBBcHjYoAG8tAG0ABSgwySgAa0SAezgoMgBdXXhkdGwcLWU0zIBFPILs6oq2yoBfAib0gFoKCDRpEskMECg2jqgu3TJegaHsweVlSQgANwgKLSY-PszikDKKyr8A8GhQiOjYs1TssIBjQu4iB8iY+MSySYx38gWg2XCAFsImF9gA1TDUVJYYDhSQAcygAB8oJIUJCogdsVAoiUSrE0AUcSgZBA4PkINJiXiMBgbmDFlCYfsEkkXuwMspMnEFudLlVlsLKtZjHYIWFocBYRAERgkWoRbodvsKCczqVylV2XdOQruRB+YsPl8yER5YrlbygSDbkEElFoo1lMIjLY5Ci0ejNvIZX6oAH8pj2AByaODFjsH02ExQAAGABI0vk4ETaH1M9miQAlPqplxQd2eotEDNpPD5tK0UMpgAyJQA7gd3mhGDhaCw1LGnNHhtGG23OxRu73+6WmB55DdXdBK1EnZbBXMRfkigaKlAexXm3YIxiHKucHEWLo4t0VreTsagkWIJCSvsYLTRBustuCmLDQPf1UUjY85DiIY1G1A4hi1fFCQoMCK0g3E9hgrYnCwEACXJJCIIwqC0MODCbzvKVH38AB6Sjhlouj6IYxjhlBE0AGUShQKdoAFLQxgmKYZigWIMWAAALOCCQOPxlDgMldFPdFpJJNAKF0fhBCgXYSjCaQlPyRhgE-DAGVYyIKGAeSQIxJSAC9P2kMRLMDJTMjCCSEO6BSKOXCoVPRCBgFSF83w-L9LwgD010BHB7XNHBfieC12M495mGBEFqK2AA9FQWKCAAJYBIQwUyQFiAARCB3gwFS0CVKYgtfd8IHssRwsi9dYqVfZ4vCP5ngAYVY4bQAqqqaooOqwimdKMso7LcqAA | |
*/ | |
/** | |
* Generic that makes it possible to compare two types, enabling complex | |
* conditionals based on otherwise unsupported features, like readonly. | |
*/ | |
type IfEquals<A, B, True, False> = (<T>() => T extends A | |
? 1 | |
: 2) extends <T>() => T extends B ? 1 : 2 | |
? True | |
: False; | |
/** | |
* Extracts the keys of an object type that are not readonly. | |
*/ | |
type WritableKeys<T> = { | |
[P in keyof T]: IfEquals< | |
{[Q in P]: T[Q]}, | |
{-readonly [Q in P]: T[Q]}, | |
P, | |
never | |
>; | |
}[keyof T]; | |
/** | |
* Removes all readonly properties from an object type. | |
*/ | |
type Writable<T> = Pick<T, WritableKeys<T>>; | |
/** | |
* All the possible primitive values in JavaScript. | |
*/ | |
type PrimitiveValue = string | number | boolean | undefined | null; | |
/** | |
* Extracts the keys of an object type that have primitive value types. | |
* | |
* Useful to filter out functions and other non-primitives. | |
*/ | |
type PrimitiveKeys<T> = { | |
[K in keyof T]: T[K] extends PrimitiveValue ? K : never; | |
}[keyof T]; | |
/** | |
* Removes all non-primitive properties from an object type, like functions, | |
* RegExp, Map, Set, etc. | |
*/ | |
type Primitive<T> = Pick<T, PrimitiveKeys<T>>; | |
/** | |
* Takes a string literal type and converts it to kebab-case. | |
*/ | |
type Kebab< | |
T extends string, | |
A extends string = '', | |
> = T extends `${infer F}${infer R}` | |
? Kebab<R, `${A}${F extends Lowercase<F> ? '' : '-'}${Lowercase<F>}`> | |
: A; | |
/** | |
* Converts all string keys of an object type to kebab-case. | |
* | |
* Useful when working with CSS properties between JS and CSS. | |
*/ | |
type KebabKeys<T> = { | |
[K in keyof T as K extends string ? Kebab<K> : K]: T[K]; | |
}; | |
/** | |
* Removes all index signatures from an object type. | |
*/ | |
type RemoveIndex<T> = { | |
[K in keyof T as string extends K | |
? never | |
: number extends K | |
? never | |
: symbol extends K | |
? never | |
: K]: T[K]; | |
}; | |
//---------------------- | |
type Source = { | |
readonly length: number; | |
foo: string; | |
bar: () => void; | |
insetInlineStart: string; | |
zIndex: string; | |
[i: number]: string; | |
}; | |
type Target = RemoveIndex<KebabKeys<Primitive<Writable<Source>>>>; | |
type HtmlStyleDeclaration = RemoveIndex<KebabKeys<Primitive<Writable<CSSStyleDeclaration>>>>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Test Playground