How strict is Typescript strict mode?

How strict is Typescript strict mode?

Preface

"use strict"Instructions JavaScript 1.8.5 (ECMAScript5)are added in.

So far, front-end er have basically turned on strict mode coding by default.

So, did you know Typescriptthat there is actually its own strict mode?

1. TypescriptStrict mode rules

When Typescriptstrict mode is set on, it will use strictstrict rules under the type of family to all files in the project code verification. The rules are:

Variables or function parameters are not allowed to have implicit any type.

2.noImplicitAny

This rule does not allow variables or function parameters to have implicit anytypes. Consider the following example:

//Javascript/Typescript non-strict mode
function extractIds (list) {
  return list.map(member => member.id)
}

The above example does not listrestrict the type and maploop the itemformal parameters member. In Typescriptstrict mode, the following error will appear:

//Typescript strict mode
function extractIds (list) {
 //❌ ^^^^
 //Parameter'list' implicitly
 //has an'any' type. ts(7006)
  return list.map(member => member.id)
 //❌ ^^^^^^
 //Parameter'member' implicitly
 //has an'any' type. ts(7006)
}

The correct wording should be:

//Typescript strict mode
interface Member {
  id: number
  name: string
}

function extractIds (list: Member[]) {
  return list.map(member => member.id)
}

1.1 How to deal with the browser's own events?

The browser comes with events, for example e.preventDefault(), the key code that prevents the default behavior of the browser.

This Typescriptwill report an error in strict mode:

//Typescript strict mode
function onChangeCheckbox (e) {
 //❌ ^
 //Parameter'e' implicitly
 //has an'any' type. ts(7006)
  e.preventDefault()
  const value = e.target.checked
  validateCheckbox(value)
}

If you need to use this type normally Web API, you need to define the extension globally. such as:

//Typescript strict mode
interface ChangeCheckboxEvent extends MouseEvent {
  target: HTMLInputElement
}

function onChangeCheckbox (e: ChangeCheckboxEvent) {
  e.preventDefault()
  const value = e.target.checked
  validateCheckbox(value)
}

1.2 The third-party library also needs to define the type

Please note that if a non- Typescriptlibrary is imported , this will also cause an error because the type of the imported library is any.

//Typescript strict mode
import {Vector} from'sylvester'
//❌ ^^^^^^^^^^^
//Could not find a declaration file
//for module'sylvester'.
//'sylvester' implicitly has an'any' type.
//Try `npm install @types/sylvester`
//if it exists or add a new declaration (.d.ts)
//file containing `declare module'sylvester';`
//ts(7016)

This may be Typescripta big trouble for the refactored version of the project , and you need to specifically define the interface type of the third-party library

3.noImplicitThis

This rule does not allow the thiscontext to be implicitly defined. Consider the following example:

//Javascript/Typescript non-strict mode
function uppercaseLabel () {
  return this.label.toUpperCase()
}

const config = {
  label:'foo-config',
  uppercaseLabel
}

config.uppercaseLabel()
//FOO-CONFIG

In non-strict mode, thispoints to the configobject. this.labelJust search config.label.

However, thismaking references on functions may be ambiguous :

//Typescript strict mode
function uppercaseLabel () {
  return this.label.toUpperCase()
 //❌ ^^^^
 //'this' implicitly has type'any'
 //because it does not have a type annotation. ts(2683)
}

If executed separately this.label.toUpperCase(), an error will be reported because the thiscontext configno longer exists, because it is labelundefined.

One way to solve this problem is to avoid thisusing functions without context:

//Typescript strict mode
const config = {
  label:'foo-config',
  uppercaseLabel () {
    return this.label.toUpperCase()
  }
}

A better way is to write interfaces and define all types instead Typescriptof inferring:

//Typescript strict mode
interface MyConfig {
  label: string
  uppercaseLabel: (params: void) => string
}

const config: MyConfig = {
  label:'foo-config',
  uppercaseLabel () {
    return this.label.toUpperCase()
  }
}

4.strictNullChecks

This rule does not allow the possibility of nullor undefined. Consider the following example:

//Typescript non-strict mode
function getArticleById (articles: Article[], id: string) {
  const article = articles.find(article => article.id === id)
  return article.meta
}

TypescriptIn non-strict mode, there will be no problems with writing in this way. But strict mode will give you something to do:

"You can't do this, what if it finddoesn't match any value?":

//Typescript strict mode
function getArticleById (articles: Article[], id: string) {
  const article = articles.find(article => article.id === id)
  return article.meta
 //❌ ^^^^^^^
 //Object is possibly'undefined'. ts(2532)
}

"I star you a star!"

So you will change to the following:

//Typescript strict mode
function getArticleById (articles: Article[], id: string) {
  const article = articles.find(article => article.id === id)
  if (typeof article ==='undefined') {
    throw new Error(`Could not find an article with id: ${id}.`)
  }

  return article.meta
}

"It's so fragrant!"

5.strictPropertyInitialization

This rule will verify the properties defined before and after the internal initialization of the constructor.

It is necessary to ensure that the properties of each instance have initial values, which can be assigned in the constructor or when the properties are defined.

( strictPropertyInitialization, This stinky name resembles Reactmany willful attributes in the source code)

Consider the following example:

//Typescript non-strict mode
class User {
  username: string;
}

const user = new User();

const username = user.username.toLowerCase();

If strict mode is enabled, the type checker will report further errors:

class User {
  username: string;
 //❌ ^^^^^^
 //Property'username' has no initializer
 //and is not definitely assigned in the constructor
}

const user = new User();
/
const username = user.username.toLowerCase();
//❌ ^^^^^^^^^^^^
//TypeError: Cannot read property'toLowerCase' of undefined

There are four solutions.

Option #1: Allowundefined

usernameProvide a undefinedtype for the attribute definition :

class User {
  username: string | undefined;
}

const user = new User();

usernameThe attribute can be a string | undefinedtype, but when written like this, you need to ensure that the value is a stringtype when you use it :

const username = typeof user.username === "string"
  ? user.username.toLowerCase()
  : "n/a";

This too is not Typescripta.

Scenario #2: Explicitly initialize attribute values

This method is a bit stupid, but it works:

class User {
  username = "n/a";
}

const user = new User();

//OK
const username = user.username.toLowerCase();

Scenario #3: Assign values ​​in the constructor

The most useful solution is to usernameadd parameters to the constructor and then assign them to usernameproperties.

In this way, new User()you must provide a default value as a parameter whenever you want:

class User {
  username: string;

  constructor(username: string) {
    this.username = username;
  }
}

const user = new User("mariusschulz");

//OK
const username = user.username.toLowerCase();

It can be publicfurther simplified by modifiers:

class User {
  constructor(public username: string) {}
}

const user = new User("mariusschulz");

//OK
const username = user.username.toLowerCase();

Scenario #4: Explicitly assign assertions

In some scenarios, properties will be initialized indirectly (using helper methods or dependency injection libraries).

In this case, you can use explicit assignment assertions on the attributes to help the type system identify types.

class User {
  username!: string;

  constructor(username: string) {
    this.initialize(username);
  }

  private initialize(username: string) {
    this.username = username;
  }
}

const user = new User("mariusschulz");

//OK
const username = user.username.toLowerCase();

By usernameadding an explicit assignment assertion to the property, we tell the type checker:, usernameeven if it cannot detect the property itself, it can expect the property to be initialized.

6.strictBindCallApply

This rule will be bind, call, applymore rigorous to detect types.

What do you mean? Consider the following example:

//JavaScript
function sum (num1: number, num2: number) {
  return num1 + num2
}

sum.apply(null, [1, 2])
//3

When you don't remember the parameter type, the parameter type and quantity will not be verified in non-strict mode. When running the code, Typescriptand the environment (probably the browser) will not cause an error:

//Typescript non-strict mode
function sum (num1: number, num2: number) {
  return num1 + num2
}

sum.apply(null, [1, 2, 3])
//Or... 3?

In Typescriptstrict mode, this is not allowed:

//Typescript strict mode
function sum (num1: number, num2: number) {
  return num1 + num2
}

sum.apply(null, [1, 2, 3])
//❌ ^^^^^^^^^
//Argument of type'[number, number, number]' is not
//assignable to parameter of type'[number, number]'.
//Types of property'length' are incompatible.
//Type '3' is not assignable to type '2'. ts(2345)

What should I do? “...”The spread operator and reduceold friends come to the rescue :

//Typescript strict mode
function sum (...args: number[]) {
  return args.reduce<number>((total, num) => total + num, 0)
}

sum.apply(null, [1, 2, 3])
//6

7. strictFunctionTypes

This rule will check and restrict the function type parameters to be resistant to variation ( contravariantly) rather than double variation ( bivariantly, that is, covariance or resistance to variation).

At first glance, the inner OS: "What is this?", here is an introduction:

What are covariance and contravariance? [1]

The covariance and contravariance wikis are very complicated, but in summary, there is actually one principle.

  • Subtypes can be implicitly converted to supertypes

For the easiest example, the relationship intwith the floattwo types can be written as follows. intfloat: That intis floatto say the subtype of it.

This stricter check applies to all function types except method or constructor declarations. Methods are specifically excluded to ensure that generic classes and interfaces (such as Array) remain covariant in general.

Consider the following example of a supertype Animalof Dogsum Cat:

declare let f1: (x: Animal) => void;
declare let f2: (x: Dog) => void;
declare let f3: (x: Cat) => void;
f1 = f2;//Error when enabling --strictFunctionTypes
f2 = f1;//correct
f2 = f3;//error
  1. The first assignment statement is allowed in the default type checking mode, but will be flagged as an error in the strict function typing mode.
  2. The strict function typing mode marks it as an error because it cannot be justified.
  3. In any mode, the third assignment is wrong because it is never reasonable.

To describe this example in another way, Tthe type (x: T) => voidis double-variable in the default type checking mode , but it Tis resistant to variation in the strict function type mode :

interface Comparer<T> {
    compare: (a: T, b: T) => number;
}

declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;

animalComparer = dogComparer;//error
dogComparer = animalComparer;//correct

At this point, a rookie front end was forced to death.

Summary & Reference

Reference article:

  1. How strict is Typescript's strict mode?[2]
  2. How should we understand the covariant contravariance in programming languages? [3]
  3. TypeScript strict function type [4]

During the interview, I am often asked why it works Typescriptbetter JavaScript?

From these strict mode rules, you can get a glimpse of the mystery, which is strict today, bugs will be thrown away in seconds , oh yeah.

Reference

[1]

What are covariance and contravariance? : https://www.stephanboyer.com/post/132/what-are-covariance-and-contravariance

[2]

How strict is Typescript's strict mode?: https://medium.com/swlh/how-strict-is-typescripts-strict-mode-f36a4d1a948a

[3]

How should we understand the covariant contravariance in programming languages? : https://www.zhihu.com/question/38861374

[4]

TypeScript strict function type: https://www.tslang.cn/docs/release-notes/typescript-2.6.html

Reference: https://cloud.tencent.com/developer/article/1534337 How strict is Typescript strict mode? -Cloud + Community-Tencent Cloud