StrictNullChecks in TypeScript introduces stricter type checks for null & undefined. This option was introduced in TypeScript 2.0. We enable it by setting strictNullChecks: true
in the tsconfig.json
, which ensures that the compiler will raise an error if we assign Null and Undefined to other types.
Table of Contents
Strict null checks
Null and Undefined are special values that represent no value. We can assign them to all other types like numbers, strings, etc. Such assignment has been the source of a lot of errors like Uncaught TypeError: Cannot read property ‘propertyName’ of undefined/null
etc, in JavaScript. This error is thrown when we try to access a property on an undefined
or null
object.
TypeScript prior to version 2.0 never warned us when we tried to access a property of the null or undefined object. In the following example, passing null in sayHello
function does not throw any compile-time error. But when you run the code you will get an (Cannot read properties of null
) error.
1 2 3 4 5 6 7 8 9 10 11 12 13 | type person = { id:number, name:string } function sayHello(p:person) { console.log(`Hello ${p.name}`) // Cannot read properties of null (reading 'name') } sayHello(null); |
Enabling StrictNullChecks
To enable StrictNullChecks open tsconfg.sys and add "strictNullChecks": true
under the compilerOptions
.
1 2 3 4 5 6 7 | { "compilerOptions": { "strictNullChecks": true }, } |
With this set to true
, you’ll get a type error if you try to use null or undefined wherever Typescript expects a concrete type.
For Example, code from the previous section results in a compiler error Argument of type 'null' is not assignable to parameter of type
. This enables us to fix the error and does not have to wait for the code to run and crash.
1 2 3 4 5 6 7 8 9 10 11 12 13 | type person = { id:number, name:string } function sayHello(p:person) { console.log(`Hello ${p.name}`) } sayHello(null); //Argument of type 'null' is not assignable to parameter of type 'person'. |
Null & Undefined under StrictNullChecks
- The
undefined
is assignable only toany
,void
&undefined
- Nothing is assignable to
undefined
exceptnever
,any
&undefined
null
is assignable only toany
&null
- Nothing is assignable to
null
except anothernull
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | let strVar: string; strVar= "Rahul"; //ok strVar= undefined; //Type 'undefined' is not assignable to type 'string' strVar= null; //Type 'null' is not assignable to type 'string' let numVar: number; numVar= 24; //ok numVar= undefined; //Type 'undefined' is not assignable to type 'number' numVar= null; //Type 'null' is not assignable to type 'number' let anyVar:any anyVar= 24; //ok anyVar= undefined; //ok anyVar= null; //ok let nullVar:null nullVar= undefined; //type 'undefined' is not assignable to type 'null' nullVar= null; //ok let unVar:undefined unVar= undefined; //ok unVar= null; //Type 'null' is not assignable to type 'undefined' |
The following section lists some of the common errors that arise when you enable strict null checks and solutions for them.
Variable foo is used before being assigned.
Trying to use a variable before initializing it will result in a compiler error.
1 2 3 4 | let numVar: number console.log(numVar) //Variable 'numVar' is used before being assigned. |
The solution is to assign a value to it at the time of initialization.
1 2 3 4 | let numVar1: number=0 console.log(numVar1) |
Assigning null or undefined
With StrictNullChecks set to true, we cannot assign null or undefined other data types. But this is not practical in real life.
In the following example, we cannot assign null
or undefined
to numVar
.
1 2 3 4 5 | let numVar=0; numVar=null //Type 'null' is not assignable to type 'number'. numVar=undefined //Type 'undefined' is not assignable to type 'number'. |
To allow a variable to accept null or undefined, we need to explicitly set it. We can do that by creating a union type.
1 2 3 4 5 6 7 8 9 10 11 12 | let numVar1: number|null=0; //union of number & null numVar1=null //ok let numVar2: number|undefined=0; //union of number & undefined numVar2=undefined //ok let numVar3: number|null|undefined=0; //union of number, null & undefined numVar3=null //ok numVar3=undefined //ok |
You can use a similar strategy when you want to pass null to a function as an argument.
1 2 3 4 5 6 7 8 9 10 11 12 13 | type person = { id:number, name:string } function sayHello(p:person|null) { //p is union type of person & null console.log(`Hello ${p.name}`) } sayHello(null); |
To pass undefined, you can also make use of the optional parameter ?
1 2 3 4 5 6 7 8 | function sayHello(p?:person) { //p is optional now. Its default value is undefined console.log(`Hello ${p.name}`) } sayHello(undefined); //ok sayHello(); //ok. You can omit p. Undefined is assigned to person |
You can also create a union type of person and undefined. But here you must pass a value for the person. not Passing a value will result in an error.
1 2 3 4 5 6 7 8 | function sayHello(p:person|undefined) { console.log(`Hello ${p.name}`) } sayHello(undefined); //ok sayHello(); //Error Expected 1 arguments, but got 0. |
This will allow all three but you must pass a value
1 2 3 | function sayHello(p:person|undefined|null) { |
is will allow all three & person is given the value undefined
if no value is passed
1 2 3 | function sayHello(p?:person|null|undefined) { |
Object is Possibly undefined / Null
Passing null or undefined to a function and using it will result in the Object is possibly null or Object is possibly undefined error.
For Example, the following code results in the compiler error at the console.log statement. Since nulls are allowed, the TypeScript compiler thinks that the p
could be possibly null.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type person = { id:number, name:string } let gates:person = {id:1, name:"Bill Gates"} function sayHello(p:person|null) { console.log(`Hello ${p.name}`) //Object is possibly 'null'. } sayHello(gates); |
There are a few ways in which you can solve the above problem.
Use if to check for null
If condition to check for null or undefined. Since we checked for null in the if condition, it is guaranteed that the p
will never be null in the else statement. The TypeScript compiler is smart enough to understand and this does not throw any errors. This behavior is known as Type Guards.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | type person = { id:number, name:string } let gates:person = {id:1, name:"Bill Gates"} function sayHello(p:person|null) { if (p===null) { //Here p is null console.log(`person is null`) } else { //This code executes only when p is not null console.log(`Hello ${p.name}`) } } sayHello(gates); // "Hello Bill Gates" sayHello(null); //"person is null" |
Non-null assertion operator!
! is a Non-null assertion operator introduced in TypeScript 2.0. We use this operator to inform the Type checker that its operand is not null or undefined.
The code below p!
tells the compiler that p
is not null or undefined. Hence the compiler does not throw any errors.
1 2 3 4 5 6 7 | function sayHello(p:person|null) { console.log(`Hello ${p!.name}`) } sayHello(gates); // "Hello Bill Gates" |
But if you actually pass a null object, then you will get a run time error. Hence use this operator only if you are very sure that it will never be null or undefined.
1 2 3 4 5 6 7 8 | function sayHello(p:person|null) { console.log(`Hello ${p!.name}`) } sayHello(gates); // "Hello Bill Gates" sayHello(null); //Cannot read properties of null |
Use Optional Chaining using?
Using optional chaining causes the run time to stop the execution if it encounters a null or undefined and return undefined.
For example in the code below, if the foo
is defined then the bar.baz()
is computed. But if foo
is null or undefined then run time will stop the further execution and it returns undefined
.
1 2 3 | let x = foo?.bar.baz(); |
Hence In the following code when we pass null, no errors are thrown. But when p is null p?.name
returns with the value undefined.
1 2 3 4 5 6 7 8 | function sayHello(p:person|null) { console.log(`Hello ${p?.name}`) } sayHello(gates); //"Hello Bill Gates" sayHello(null); //Hello undefined |
Nullish Coalescing
The nullish coalescing operator (??
) is a logical operator that takes two arguments. It returns the right-hand side operand when its left-hand side operand is null
or undefined
. otherwise, it returns its left-hand side operand.
The Nullish Coalescing can be used to provide a default value if the expression is null.
In the following code if foo
is null (or undefined) then the code will return bar()
else it will return foo
1 2 3 | let x = foo ?? bar(); |
References
Read More