Skip to content

About the author of Daydream Drift

Tomasz Niezgoda (LinkedIn/tomaszniezgoda & GitHub/tniezg) is the author of this blog. It contains original content written with care.

Please link back to this website when referencing any of the materials.

Author:

Common helper types not (yet) standardised

Published

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
	)
}