Skip to content

Instantly share code, notes, and snippets.

@emileber
Last active March 14, 2024 18:28
Show Gist options
  • Save emileber/1efac6ba2d2801ce5963234ed0f3208f to your computer and use it in GitHub Desktop.
Save emileber/1efac6ba2d2801ce5963234ed0f3208f to your computer and use it in GitHub Desktop.
TypeScript helpers
/**
* @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>>>>;
@emileber
Copy link
Author

emileber commented Mar 13, 2024

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