@danwithabox/nullish
Version:
Utilities that pair well with the lovely nullish coalescing (??) operator
155 lines (116 loc) • 5.22 kB
Markdown
# /nullish
Utilities that pair well with the lovely [nullish coalescing (`??`) operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing).
[](https://opensource.org/licenses/MIT)
## Install
```bash
$ npm install /nullish --save
```
## Overview
The provided utilities are:
- [`nullishMap(value, mapFn)`](#nullishmapvalue-mapfn) - Map a nullish value as if it were non-nullish.
- [`nullishOf(value)`](#nullishofvalue) - Augment a value's type with `null` and `undefined`.
### `nullishMap(value, mapFn)`
```ts
import { nullishMap } from "@danwithabox/nullish";
// Instead of expecting unsafe data,
type Data = { foo: number, bar: number, } /* | null | undefined */;
// define operations on safe data,
function processSafeData(data: Data): number {
return data.foo + data.bar;
}
// then bridge the safety gap with `nullishMap()`
function handleUnsafeData(unsafeData: Data | null | undefined): number {
return nullishMap(unsafeData, processSafeData) ?? 0;
}
// or use it in any other way to make more concise code
function greet(data?: { name: string, surname: string, } | null): string {
return nullishMap(data, data => `Hello, ${data.name} ${data.surname}!`) ?? `Hi there!`;
// Note the lack of branches and optional chaining (?.) operators, it's just one succinct line
}
console.log(greet({ name: `Daniel`, surname: `Withabox`, }));
// Hello, Daniel Withabox!
console.log(greet());
// Hi there!
```
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
```
### `nullishOf(value)`
```ts
import { nullishMap, nullishOf } from "@danwithabox/nullish";
// Optional value declaration: the type is inferred and made optional without any manual typing
let complicatedDeclaration = nullishOf({
foo: 1,
bar: { baz: 2, },
});
function calculate(): number {
// Handle the value as if it weren't optional
return nullishMap(complicatedDeclaration, _ => _.foo + _.bar.baz) ?? 0;
}
console.log(calculate());
// 3
complicatedDeclaration = null; // assignment does not cause a type error
console.log(calculate());
// 0
```
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
```
## Acknowledgements
Motivation to release this, and a realization that not only I needed such a thing, came from:
- [javascript - Opposite of nullish coalescing operator - Stack Overflow](https://stackoverflow.com/questions/62929428/opposite-of-nullish-coalescing-operator)
- ['maybe' operator to complement ?? operator - 💡 Ideas - TC39](https://es.discourse.group/t/maybe-operator-to-complement-operator/200)
- [Optional projection / expression operator - e.g. maybeNullish ?:: transform(maybeNullish) - 💡 Ideas - TC39](https://es.discourse.group/t/optional-projection-expression-operator-e-g-maybenullish-transform-maybenullish/572)
- [[syntax] Inverse null coalescing operator - 💡 Ideas - TC39](https://es.discourse.group/t/syntax-inverse-null-coalescing-operator/547)