Null and Undefined

JavaScript (and by extension TypeScript) has two bottom types : null and undefined. They are intended to mean different things:

  • Something hasn't been initialized : undefined.
  • Something is currently unavailable: null.

Checking for either

Fact is you will need to deal with both. Just check for either with == check.

/// Imagine you are doing `foo.bar == undefined` where bar can be one of:
console.log(undefined == undefined); // true
console.log(null == undefined); // true

// You don't have to worry about falsy values making through this check
console.log(0 == undefined); // false
console.log('' == undefined); // false
console.log(false == undefined); // false

Recommend == null to check for both undefined or null. You generally don't want to make a distinction between the two.

function foo(arg: string | null | undefined) {
  if (arg != null) {
    // arg must be a string as `!=` rules out both null and undefined. 
  }
}

One exception, root level undefined values which we discuss next.

Checking for root level undefined

Remember how I said you should use == null. Of course you do (cause I just said it ^). Don't use it for root level things. In strict mode if you use foo and foo is undefined you get a ReferenceError exception and the whole call stack unwinds.

You should use strict mode ... and in fact the TS compiler will insert it for you if you use modules ... more on those later in the book so you don't have to be explicit about it :)

So to check if a variable is defined or not at a global level you normally use typeof:

if (typeof someglobal !== 'undefined') {
  // someglobal is now safe to use
  console.log(someglobal);
}

Limit explicit use of undefined

Because TypeScript gives you the opportunity to document your structures separately from values instead of stuff like:

function foo(){
  // if Something
  return {a:1,b:2};
  // else
  return {a:1,b:undefined};
}

you should use a type annotation:

function foo():{a:number,b?:number}{
  // if Something
  return {a:1,b:2};
  // else
  return {a:1};
}

Node style callbacks

Node style callback functions (e.g. (err,somethingElse)=>{ /* something */ }) are generally called with err set to null if there isn't an error. You generally just use a truthy check for this anyways:

fs.readFile('someFile', 'utf8', (err,data) => {
  if (err) {
    // do something
  } else {
    // no error
  }
});

When creating your own APIs it's okay to use null in this case for consistency. In all sincerity for your own APIs you should look at promises, in that case you actually don't need to bother with absent error values (you handle them with .then vs. .catch).

Don't use undefined as a means of denoting validity

For example an awful function like this:

function toInt(str:string) {
  return str ? parseInt(str) : undefined;
}

can be much better written like this:

function toInt(str: string): { valid: boolean, int?: number } {
  const int = parseInt(str);
  if (isNaN(int)) {
    return { valid: false };
  }
  else {
    return { valid: true, int };
  }
}

Final thoughts

TypeScript team doesn't use null : TypeScript coding guidelines and it hasn't caused any problems. Douglas Crockford thinks null is a bad idea and we should all just use undefined.

However NodeJS style code bases uses null for Error arguments as standard as it denotes Something is currently unavailable. I personally don't care to distinguish between the two as most projects use libraries with differing opinions and just rule out both with == null.

results matching ""

    No results matching ""