@danwithabox/nullish v0.1.1
@danwithabox/nullish
Utilities that pair well with the lovely nullish coalescing (??) operator.
Install
$ npm install @danwithabox/nullish --saveOverview
The provided utilities are:
nullishMap(value, mapFn)- Map a nullish value as if it were non-nullish.nullishOf(value)- Augment a value's type withnullandundefined.
nullishMap(value, mapFn)
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 | undefinedconst val = 0 as number | null | undefined; const res = nullishMap(val, val => `${val}`); // ^ string | null | undefinedT | undefined=>R | undefinedconst val = 0 as number | undefined; const res = nullishMap(val, val => `${val}`); // ^ string | undefinedT | null=>R | nullconst val = 0 as number | null; const res = nullishMap(val, val => `${val}`); // ^ string | nullT=>R(not terribly useful, but it's allowed for simplicity's sake)const val = 0 as number; const res = nullishMap(val, val => `${val}`); // ^ string
nullishOf(value)
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());
// 0Augment 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:
let optional = nullishOf({ foo: 1, bar: 2, }) ?? void 0; // ^ { foo: number; bar: number; } | undefinedSafely accessing arrays without enabling
noUncheckedIndexedAccessintsconfig.json: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: