@danwithabox/nullish
Version:
Utilities that pair well with the lovely nullish coalescing (??) operator
99 lines (96 loc) • 2.92 kB
TypeScript
/**
* Map a nullish value as if it were non-nullish.
*
* The result retains either `null`, or `undefined`, or both, or neither, depending on what's inferred from the input value.
*
* Practically, the possible mappings are:
*
* ---
*
* `T | null | undefined` => `R | null | undefined`
* ```ts
* const val = 0 as number | null | undefined;
* const res = nullishMap(val, val => `${val}`);
* // ^ string | null | undefined
* ```
*
* ---
*
* `T | undefined` => `R | undefined`
* ```ts
* const val = 0 as number | undefined;
* const res = nullishMap(val, val => `${val}`);
* // ^ string | undefined
* ```
*
* ---
*
* `T | null` => `R | null`
* ```ts
* const val = 0 as number | null;
* const res = nullishMap(val, val => `${val}`);
* // ^ string | null
* ```
*
* ---
*
* `T` => `R` _(not terribly useful, but it's allowed for simplicity's sake)_
* ```ts
* const val = 0 as number;
* const res = nullishMap(val, val => `${val}`);
* // ^ string
* ```
*
*/
declare function nullishMap<T, R>(value: T, mapFn: (value: NonNullable<T>) => R): NullishMap<T, R>;
/**
* Union of `R`, and either `null`, `undefined`, or both, depending on which of the two are constituents of `T`.
*
* @see {@link nullishMap}
*/
type NullishMap<T, R> = T extends null ? null : T extends undefined ? undefined : R;
/**
* Augment a value's type with `null` and `undefined`.
*
* Zero performance impact at runtime, as it is simply an identity function, and it most likely gets inlined.
*
* Useful in a few common situations:
*
* ---
*
* Making an inferred type optional at variable declaration, since something like https://github.com/microsoft/TypeScript/issues/13321 is not yet possible:
* ```ts
* let optional = nullishOf({ foo: 1, bar: 2, }) ?? void 0;
* // ^ { foo: number; bar: number; } | undefined
* ```
* ---
* Safely accessing arrays without enabling `noUncheckedIndexedAccess` in `tsconfig.json`:
* ```ts
* const myArray = [0, , 2].map(n => Boolean(n));
*
* // Without `noUncheckedIndexedAccess`:
* let element = myArray[1];
* // ^ `boolean`
* // this is incorrect, due to the empty element
*
* // With manual typing:
* let maybeElement1 = myArray[1] as undefined | (typeof myArray)[number];
* // ^ `boolean | undefined`
* // correct, but a hassle to type
*
* // With `nullishOf`:
* let maybeElement2 = nullishOf(myArray[1]);
* // ^ `boolean | null | undefined`
* // correct enough: it has an extraneous `null`, but that's fine in most situations
*
* // And if you want to narrow to either `null` or `undefined`:
* let maybeElement3 = nullishOf(myArray[1]) ?? null;
* // ^ `boolean | null`
* // correct
* let maybeElement4 = nullishOf(myArray[1]) ?? void 0;
* // ^ `boolean | undefined`
* // correct
* ```
*/
declare function nullishOf<T>(value: T): T | null | undefined;
export { type NullishMap, nullishMap, nullishOf };