Miguel Moura
Miguel Moura

Reputation: 39484

Check if an object's properties are null in Typescript

In Typescript I am checking if an object's properties are null as follows:

var addressCountryName = response.address == null 
   ? null 
   : response.address.country == null ? null : response.address.country.name;

Both addresses and country can be null ... I also tried:

var addressCountryName = response.address?.country?.name;

This does not seem to work ...

Is there a shortest way to check for null / undefined?

Upvotes: 5

Views: 11680

Answers (3)

Jesse Carter
Jesse Carter

Reputation: 21207

As others have mentioned the null coalescing operator is still in a proposal stage so you can't access properties using ?. Your only options currently are to do deep nesting with if statements or ternarys or to consider something like the get method from lodash which would let you do:

let addressCountryName = get(response, 'address.country.name');

Which will either return the value of name or undefined if any part of the object path was null or undefined.

Here's a CodeSandbox with an example of using lodash: https://codesandbox.io/s/mm46wkr058

Upvotes: 2

jcalz
jcalz

Reputation: 330086

There is currently no null coalescing operator in JavaScript. There are different ways to get around it (like your nested ternary types). If you're willing to shove helper types and functions into a library somewhere, you could do this:

type MyResponse = {
  address?: null | {
    country?: null | {
      name?: null | string
    }
  }
}

let response: MyResponse = Math.random() < 0.5 ? 
  { address: { country: { name: "France" } } } : { address: null }

var addressCountryName = nullSafe(response, r => r.address.country.name); 
// string | undefined | null

Where nullSafe() is defined like this:

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];
}

Yes, it's a mess, that's why I said to shove it into a library. It works by making a Proxy that always allows you to drill down into properties even if it is essentially null.

Anyway, it's one option. Good luck!

Upvotes: 4

Calidus
Calidus

Reputation: 1414

! is the non-null assertion operator in Typescript. ? is for Angular html templates.

Upvotes: 0

Related Questions