I would agree with the answer by Tim Perry with regards to using only one - either null or undefined - and my advice is also to stick with undefined, mostly because you cannot avoid it as this is the value of missing fields and uninitialized variables.
Also null doesn't work for default function parameters:
function f(x = 1) {
// ...
}
Here f(undefined)
will set x
to 1
but f(null)
will not!
The only argument for null is that JSON doesn't support explicit undefined but this is not such a problem as for those cases when you deserialize JSON that needs explicit null values (as opposed to just missing values, effectively meaning undefined) you can easily convert that.
In my projects I go as far as to forbid nulls with a linter config, e.g. for TSLint:
"no-any": true,
"no-null-keyword": true
This witch strictNullChecks
works great in avoiding many kinds of errors.
Remember that the creator of NULL has called it his billion-dollar mistake. Having two nullish values is even worse. But if you have just one (like undefined) and you are using strict null checks in TypeScript then you can be pretty safe from the problems usually caused by null/undefined in most of the languages.
No matter if you go for undefined, null or both, I strongly advice you to always use strictNullChecks because without it your code is not really type safe.
See the TypeScript 2.0 release notes about null- and undefined-aware types:
The type checker previously considered null and undefined assignable to anything. Effectively, null and undefined were valid values of every type and it wasn’t possible to specifically exclude them (and therefore not possible to detect erroneous use of them). [emphasis added]
It is not enabled by default for backwards compatibility with old code that was written before the new way of checking null and undefined was added.
Consider this simple function:
const f = (x: string) => x.toLowerCase();
It is not type safe without the strict flags.
It can crash with runtime TypeError exceptions like:
TypeError: Cannot read property 'toLowerCase' of undefined
With the strict flags:
const f = (x: string | null) => x.toLowerCase(); // COMPILE TIME ERROR
const f = (x: string | null) => x && x.toLowerCase(); // OK and safe
For more info, see:
Update: Soon you will be able to use optional chaining in the example above:
const f = (x: string | null) => x?.toLowerCase(); // OK and safe
and even like this, if x
could be not null but possibly a type without .toLowerCase()
method:
const f = (x: string | number | null) => x?.toLowerCase?.(); // safe as well
For more info, see the documentation about the optional chaining operator.