Last active
January 3, 2019 18:30
-
-
Save jcalz/3e78de22e276f7e5c68f526a5e560ab2 to your computer and use it in GitHub Desktop.
Workaround for lack of null-coalescing operator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface NullSigil { | |
[k: string]: NullSigil; | |
} | |
// phantom property to recover T from NullSafe<T> | |
type OriginalTypeKey = "***originalType***" | |
type IsNullable<T, Y, N> = null extends T ? Y : | |
undefined extends T ? Y : N; | |
type NullSafe<T, N = NullSigil> = Record<OriginalTypeKey, T> & ( | |
T extends object ? { | |
[K in keyof T]-?: NullSafe<T[K], N> | |
} : IsNullable<T, NonNullable<T> | N, T> | |
) | |
type NullUnsafe<T> = | |
T extends Record<OriginalTypeKey, infer U> ? U : | |
T extends NullSigil ? null : | |
T | |
function nullSafe<T, U>( | |
val: T, | |
fn: <N extends NullSigil>(nullSafeVal: NullSafe<T, N>) => U | |
): NullUnsafe<U>; | |
function nullSafe(val: any, fn: (nullSafeVal: any) => any): any { | |
const nullSigil: NullSigil = new Proxy({} as NullSigil, { get(t, p, r) { return r } }); | |
const deproxify = Symbol("deproxify"); | |
function ns<T>(obj: T): NullSafe<T>; | |
function ns(obj: any) { | |
if ((typeof obj === "undefined") || (obj === null)) return nullSigil; | |
if (typeof obj !== "object") return obj; | |
return new Proxy(obj, { get(t, p, r) { return (p === deproxify) ? t : (p in t) ? ns(t[p]) : nullSigil } }); | |
} | |
const ret: any = fn(ns(val)); | |
if (ret === nullSigil) return null; | |
if (typeof ret !== "object") return ret; | |
return ret[deproxify]; | |
} | |
interface WhoKnows { | |
a?: string, | |
b?: { | |
c?: string, | |
d?: number, | |
e?: boolean | |
}, | |
f?: { | |
g?: { | |
h?: 12345 | |
} | |
}, | |
i: string | |
} | |
const w: WhoKnows = { | |
a: "a", | |
b: { d: 2 }, | |
f: { g: { h: 12345 } }, | |
i: "howdy" | |
} | |
const wbc = nullSafe(w, w => w.b.c); | |
console.log(wbc); | |
const wa = nullSafe(w, w => w.a); | |
console.log(wa); | |
const wfgh = nullSafe(w, w => w.f.g.h) || 100; | |
console.log(wfgh); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment