Skip to content

Instantly share code, notes, and snippets.

@h-jennings
Last active November 4, 2021 15:12
Show Gist options
  • Save h-jennings/751f3d14eb38cff4cc034f6dc17c1a8e to your computer and use it in GitHub Desktop.
Save h-jennings/751f3d14eb38cff4cc034f6dc17c1a8e to your computer and use it in GitHub Desktop.
Helpful TS utils
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