The default TypeScript library (here's an example for ES5) contains many helpers for manipulating custom types in projects, such as Pick
, Partial
and Record
. Experience shows, however, that numerous useful types and interfaces are still missing. Below is a hand-picked list of TypeScript that didn't make the cut but is often valuable to copy into projects.
/**
* Extract the type of values of an object type.
*
* Example: `const obj = { k1: 'a', k2: 'b' }; ValueOfObject<typeof obj>`
*/
export type ValueOfObject<T> = T[keyof T]
/**
* Arrays have more keys than just indices. Therefore, they need to be indexed
* by `number` only in contrast to the generic `ValueOfObject<T>`.
*
* Example: `const someArray = ['a', 'b', 'c']; ValueOfArray<typeof someArray>`
*/
export type ValueOfArray<T extends Record<number, unknown>> = T[number]
/**
* Get the intersection of values for map.
*
* Example: `ValueInMapRecord<Map<string, { value1: string; value2: boolean }>>`
*/
export type ValueInMapRecord<MapRecord> =
MapRecord extends Map<any, infer I> ? I : never
/**
* Turns an intersection of object types ({...} & {...} & ...) into a single
* object type influencing how it's summarised by tools like IntelliSense.
*/
export type IterateComplexRecord<Source> = {
[W in keyof Source]: Source[W]
}
/**
* Prevents listed object keys from being optional.
*
* Example: `NonNullableKeysAndValues<{key1?: boolean, key2?: boolean}, 'key1'>`
*/
export type NonNullableKeysAndValues<
Source,
NNKeys extends keyof Source
> = IterateComplexRecord<
Source & { [Key in NNKeys]-?: NonNullable<Source[Key]> }
>
In some cases, TypeScript alone isn't smart enough to parse JavaScript and figure out exact types. Simultaneously, TypeScript avoids generating runtime JavaScript except in a few situations and might stop entirely in the future. Here are the examples.
One of the most common needs for TypeScript, which isn't built-in, is to identify the shape of unknown
objects. This can be achieved by including and calling the following function:
/**
* Use type narrowing to determine whether the provided `maybeObject` is
* an object and contains `wantedKey`.
*
* Example:
* `if(isObjectKey(unknownObject, 'key1')) console.log(unknownObject.key1)`
*/
const isObjectKey = <T extends PropertyKey, W extends { [key in T]: unknown }>(
maybeObject: W | unknown,
wantedKey: T
): maybeObject is W => {
return (
typeof maybeObject === 'object' &&
maybeObject !== null &&
wantedKey in maybeObject
)
}