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 to false:
{
"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 in tsconfig.json so that it is set back to true:

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 to false:
{
"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 in tsconfig.json so that it is set back to true:

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 to false:
{
"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 in tsconfig.json so that it is set back to true:

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 to false:
{
"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 in tsconfig.json so that it is set back to true:

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 to false:
{
"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 in tsconfig.json so that it is set back to true:

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 to false:
{
"compilerOptions": {
...
"strictPropertyInitialization": false
},
....
}

The type error on this property is resolved.

🤔

How would we properly resolve this type error?

  • Remove the strictPropertyInitialization field in tsconfig.json so that it is set back to true:

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 the dist folder to verify that it includes "use strict" at the top.
"use strict";
class Person ...
  • This is impacting a variable called arguments in the create function:
let arguments;

Variables can't have the name "arguments" in JavaScript strict mode.

  • Set alwaysStrict to false:
{
"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 to args.

  • Remove the alwaysStrict field in tsconfig.json so that it is set back to true:

  • 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 to true.
  • noImplicitAny - Raises an error on expressions and declarations with an inferred type of any.
  • noImplicitThis - Raises an error on this expressions with an inferred type of any.
  • strictBindCallApply - Enables strict checking of the bind, call, and apply methods on functions.
  • strictNullChecks - Means that null and undefined 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.

© 2024 Carl Rippon
Privacy Policy
This site uses cookies. Click here to find out more