Learn TypeScript
Controlling type checking strictness
Controlling type checking strictness
In this lesson, we will learn about all the compiler options that control the type checking's strictness.
Technical requirements
You will need the following installed on your computer for this lesson:
Node.js and npm. You can download these from https://nodejs.org/en/download. If you already have these installed, make sure that Node.js is at least version 8.2, and that npm is at least version 5.
Code editor such as Visual Studio Code. This can be installed from https://code.visualstudio.com.
The starter project which can be found here.
After the starter project has been downloaded. Open it in Visual Studio Code and execute the following command in a Terminal window:
npm install
This will install the project dependency, which is TypeScript.
Understanding TypeScript strict mode
The starter project has a compiler option switch on called strict
. This turns on a set of type checking rules and is referred to as strict mode. This is separate from JavaScript's strict mode.
When creating a new TypeScript project, it is recommended to have strict mode on so that code benefits from the most stringent type checking from the start of its life. However, strict mode may not be feasible when migrating a JavaScript codebase to TypeScript because of the number of type errors raised.
In addition to the strict
option, each type checking rule can be controlled via a specific compiler option. This gives fine-grain control on the type checker's strictness.
- Run the TypeScript compiler:
npm run tsc
Several type errors are raised in index.ts
. In the following sections, we will learn about the strictness compiler options and its impact on index.ts
.
Understanding noImplicitAny
The noImplicitAny
compiler option will raise errors in expressions and declarations with an implied any
type. This option is currently on because the strict
option is true
.
The greet
method is currently violating this rule because the prefix
parameter is inferred to have the any
type:
public greet(prefix) { ...}
- Set
noImplicitAny
tofalse
:
{ "compilerOptions": { ... "noImplicitAny": false }, ....}
The type error on this method is resolved.
How could we change the greet
function to no longer violate the noImplicitAny
rule? Make this change.
- Remove the
noImplicitAny
field intsconfig.json
so that it is set back totrue
:
There is still no type error on this method.
Neat!
Understanding noImplicitThis
The noImplicitThis
compiler option raises an error on expressions that reference this
with an implied any
type. This option is currently on because the strict
option is true
.
The goodbyeFunction
method is voilating this rule at the moment where the name
property within this
is accessed:
public goodbyeFunction() { return function () { console.log(`Goodbye ${this.name}`); };}
TypeScript infers this
to be of type any
and so raises a type error.
- Set
noImplicitThis
tofalse
:
{ "compilerOptions": { ... "noImplicitThis": false }, ....}
The type error is resolved.
There are two ways to resolve this error properly.
The first method of resolving the type error is to let TypeScript know the type of this
in the function signature:
public goodbyeFunction() { return function (this: Person) { ... };}
The second method of resolving the type error is to use an arrow function:
public goodbyeFunction() { return () => { ... };}
- Use one of these approaches and remove the
noImplicitThis
field intsconfig.json
so that it is set back totrue
:
There is still no type error on this code.
Nice!
Understanding strictBindCallApply
The strictBindCallApply
compiler option enables stricter checking of the bind
, call
, and apply
methods. This option is currently on because the strict
option is true
.
The call to person.calculatePrice
in the create
function is voilating this rule at the moment because calculatePrice
requires two arguments to be passed in:
const discountedPrice = person.calculatePrice.apply(undefined, ["FREE"]);
- Set
strictBindCallApply
tofalse
:
{ "compilerOptions": { ... "strictBindCallApply": false }, ....}
The type error on this code is resolved.
Properly resolve the type error by passing in a second parameter for the price.
- Remove the
strictBindCallApply
field intsconfig.json
so that it is set back totrue
:
There is still no type error on this code.
Nice!
Understanding strictNullChecks
The strictNullChecks
compiler option excludes the null
and undefined
values in the domain of every type. This option is currently on because the strict
option is true
.
The assignment of person
to null
at the end of the create
function is violating this rule at the moment because null
is not within the Person
type.
person = null;
- Set
strictNullChecks
tofalse
:
{ "compilerOptions": { ... "strictNullChecks": false }, ....}
The type error on this line is resolved.
How would we properly resolve the type error if we wanted to assign person
to null
. Make this change
- Remove the
strictNullChecks
field intsconfig.json
so that it is set back totrue
:
There is still no type error on this line.
Understanding strictFunctionTypes
The strictFunctionTypes
disables bivariant parameter checking for function types. This option is currently on because the strict
option is true
.
The getDiscountCode
function contains a type error because the info
parameter contains a token
property that is not part of APIRequestHandler
:
const getDiscountCode: APIRequestHandler<string> = async ( info: APIRequestInfo & { token: string }) => { ... }
- Set
strictFunctionTypes
tofalse
:
{ "compilerOptions": { ... "strictFunctionTypes": false }, ....}
The type error on this function is resolved.
Resolving this type error isn't straight forward. The getDiscountCode
clearly requires the token property. We could change the APIRequestHandler
type, but that might impact other code. A solution is to remove the type annotation from getDiscountCode
and let TypeScript infer its type.
Remove the type annotation from
getDiscountCode
to resolve this error.Remove the
strictFunctionTypes
field intsconfig.json
so that it is set back totrue
:
There is still no type error on this function.
Understanding strictPropertyInitialization
The strictPropertyInitialization
compiler option ensures non-undefined class properties are initialized in the constructor. This option requires strictNullChecks
to be enabled to take effect. This option is currently on because the strict
option is true
.
The name
property within the Person
type is currently violating this rule.
public name: string;
- Set
strictPropertyInitialization
tofalse
:
{ "compilerOptions": { ... "strictPropertyInitialization": false }, ....}
The type error on this property is resolved.
How would we properly resolve this type error?
- Remove the
strictPropertyInitialization
field intsconfig.json
so that it is set back totrue
:
There is still no type error on this property.
Understanding alwaysStrict
The alwaysStrict
compiler option ensures JavaScript strict mode is used type checking process. It also determines whether "use strict"
is emitted in JavaScript. This option is currently on because the strict
option is true
.
- Open
index.js
in thedist
folder to verify that it includes"use strict"
at the top.
"use strict";class Person ...
- This is impacting a variable called
arguments
in thecreate
function:
let arguments;
Variables can't have the name "arguments" in JavaScript strict mode.
- Set
alwaysStrict
tofalse
:
{ "compilerOptions": { ... "alwaysStrict": false }, ....}
The TypeScript compiler needs to be stopped and restarted for this option to take full effect.
- Stop and restart the TypeScript compiler.
The type error is resolved.
Is 'use strict'
at the top of the transpiled JavaScript file?
Rename the
arguments
variable toargs
.Remove the
alwaysStrict
field intsconfig.json
so that it is set back totrue
:Stop and restart the TypeScript compiler.
There is still no type error on this line of code.
Summary
TypeScript has a range of options for controlling how strict the type checking is:
strict
- Sets maximum strictness, setting all the flags below totrue
.noImplicitAny
- Raises an error on expressions and declarations with an inferred type ofany
.noImplicitThis
- Raises an error onthis
expressions with an inferred type ofany
.strictBindCallApply
- Enables strict checking of thebind
,call
, andapply
methods on functions.strictNullChecks
- Means thatnull
andundefined
values are not valid values in types.strictFunctionTypes
- Disables bivariant parameter checking for function types.strictPropertyInitialization
- Ensures class properties are assigned a default value or initialized in the constructor.alwaysStrict
- Parses the code in JavaScript strict mode and emits "use strict" in the transpiled code.
For new projects, it is recommended to set strict
to true
.
For JavaScript migration projects, strict
can be set to true
with the other flags to false
if the corresponding rule raises lots of type errors. A goal could be to resolve the type errors and set all the strict
flags to true
over time.
In the next lesson, we will learn about some compile options that help with code quality.