Last active
November 4, 2021 15:12
-
-
Save h-jennings/751f3d14eb38cff4cc034f6dc17c1a8e to your computer and use it in GitHub Desktop.
Helpful TS utils
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
type ArrayItemType<T extends unknown[]> = T extends unknown[] | |
? T extends (infer U)[] | |
? U | |
: never | |
: never; | |
type Entries<T> = { | |
[K in keyof T]: [K, T[K]]; | |
}[keyof T][]; | |
type Nullable<T> = T | null; | |
type PropValueNullable<T extends object> = { | |
[P in keyof T]: Nullable<T[P]>; | |
}; | |
type PropValueNonNullable<T extends object> = { | |
[P in keyof T]: NonNullable<T[P]>; | |
}; | |
/** Allows you to mark certain properties from another type as nullable */ | |
type WithNullable<T extends object, K extends keyof T> = Omit<T, K> & | |
PropValueNullable<T>; | |
/** Allows you to mark certain properties from another type as non-nullable */ | |
type WithNonNullable<T extends object, K extends keyof T> = Omit<T, K> & | |
PropValueNonNullable<T>; | |
/** | |
* Compares `T` with the provided `Structure` type. | |
* | |
* source: {@link https://fettblog.eu/typescript-match-the-exact-object-shape/} | |
*/ | |
type ValidateStructure<T, Structure> = T extends Structure | |
? Exclude<keyof T, keyof Structure> extends never | |
? T | |
: never | |
: never; | |
/** Allows you to mark certain properties from another type as optional */ | |
type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<T>; | |
/** | |
* Helper function for exhaustive checks of discriminated unions. | |
* https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html | |
* | |
* @example | |
* | |
* type A = {type: 'a'}; | |
* type B = {type: 'b'}; | |
* type Union = A | B; | |
* | |
* function doSomething(arg: Union) { | |
* if (arg.type === 'a') { | |
* return something; | |
* } | |
* | |
* if (arg.type === 'b') { | |
* return somethingElse; | |
* } | |
* | |
* // TS will error if there are other types in the union | |
* // Will throw an Error when called at runtime. | |
* // Use `assertNever(arg, true)` instead to fail silently. | |
* return assertNever(arg); | |
* } | |
*/ | |
function assertNever(value: never, noThrow?: boolean): never { | |
if (noThrow) { | |
return value; | |
} | |
throw new Error( | |
`Unhandled discriminated union member: ${JSON.stringify(value)}` | |
); | |
} | |
function normalizeArray<T extends any[], K extends ArrayItemType<T>>( | |
arr: T, | |
idKey: keyof K | |
): { [id: string]: K } { | |
return arr.reduce((acc, curr) => { | |
const value = curr[idKey]; | |
if (typeof value === 'string' || typeof value === 'number') { | |
Object.assign(acc, { [value]: curr }); | |
} | |
return acc; | |
}, {}); | |
} | |
/** | |
* Returns the key-value pairs of `TObj` and `TKey` | |
*/ | |
function getKeyValue<TObj, TKey extends keyof TObj>( | |
obj: TObj, | |
key: TKey | |
): TObj[TKey] { | |
return obj[key]; | |
} | |
/** | |
* Given an object `TObj` and a readonly array of keys `TKeys`, (must be valid keys of `TObj`) this function creates a new object of those key value pairs | |
*/ | |
function newObjectFromKeys<TObj, TKeys extends readonly (keyof TObj)[]>( | |
obj: TObj, | |
keys: TKeys | |
): Pick<TObj, TKeys[number]> { | |
return keys.reduce((newObj, key) => { | |
Object.assign(newObj, { [key]: getKeyValue(obj, key) }); | |
return newObj; | |
}, {} as Pick<TObj, TKeys[number]>); | |
} | |
/** | |
* Immutably removes (plucks) a provided property `TKey` from an object `TObj` | |
*/ | |
function pluckPropertyFromObj<TObj, TKey extends keyof TObj>( | |
obj: TObj, | |
key: TKey | |
): Omit<TObj, TKey> { | |
const { [key]: _plucked, ...rest } = obj; | |
return rest; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment