This is the twelfth day of my participation in Gwen Challenge

What is the type checking mechanism

Some of the principles and behaviors the typescript compiler adheres to when doing type checking.

What does type checking do

Assist development and improve development efficiency

Type check mechanism classification

  • Type inference
  • Type compatibility
  • Type of protection

Type inference

Instead of specifying the type of a variable or the return value type of a function,typescript can automatically infer a type for it based on certain rules

  • Basic type inference
  • Best generic type inference
  • Context type inference

Type inference for the underlying type

Usage scenario: When initializing a variable

// TS infer result: let a: number
let a = 1;

// let b: any[]
let b = [];

// let c: number[]
let c = [1];

// let d: string[]
let d = ["a"];
Copy the code

It is also useful when setting the default parameters of a function

// (parameter) x: number
let a1 = (x = 1) = > {
  return x;
};

// let c: (x? : number) => number
let c = (x = 1) = > {
  return x + 1;
};
Copy the code

It is also useful when determining the return value of a function

// let a2: (x? : number) => number
let a2 = (x = 1) = > {
  const res = x + 2;
  return res;
};
Copy the code

Best generic typeinference

When a type needs to be inferred from multiple types, TS tries to infer a common type that is compatible with all current types

// let b1: (number | null)[]
let b1 = [1.null];
Copy the code

1 and NULL are incompatible types, and TS will infer a combined type of number and NULL

Note: After you modify the following configuration in tsconfig.json, the result type check will change again

."strictNullChecks": false.Copy the code
// let b1: number[]
let b1 = [1.null];
Copy the code

conclusion

All of the above types are inferred from right to left, that is, from the value on the right side of the expression to infer the type of the variable on the left side of the expression

Context typeinference

Usually occurs in event processing,

// ts or from the left time binding, to infer the right event type
window.onkeydown = (event: KeyboardEvent) = > {
  console.log(event.AT_TARGET);
};

/* in recent versions of vscode, if you type window.onkeydown = (event) => {console.log('onkeydown')}, the editor will tell you that the parameter 'event' implicitly has type 'any' without context inference. Solution: Add KeyboardEvent */
Copy the code

TS allows developers to manually override TS inferences (type assertions) ** when THEY do not conform to expectations and the developer knows the code better than TS does

Types of assertions

let foo = {};
// Foo has no bar property on it
foo.bar = 1;
Copy the code
interface Foo {
  bar: number;
}

let foo = {} as Foo;

foo.bar = 1;
Copy the code

Type assertions can increase the flexibility of code and can be very effective in modifying some old code. However, type assertions should not be abused. You need to be aware of the context, and it is recommended to specify all types when declaring

interface Foo {
  bar: number;
}
// No errors are reported after type assertions are abused
let foo = {} as Foo;
Copy the code

Specify all types at declaration time

interface Foo {
  bar: number;
}

/* The attribute "bar" is missing from type "{}", but is required from type "Foo". Ts (2741) 4. Ts (3, 5): "bar" is declared here. * /
let foo: Foo = {};
Copy the code

To solve

interface Foo {
  bar: number;
}

let foo: Foo = {
  bar: 1};Copy the code

What type compatibility is:

When a type Y can be assigned to a type X, you can say that type X is compatible with type Y

X compatible Y: X(target type) = Y(source type)

/* config --tyconfig.json: "strictNullChecks": false, characters are compatible with null types, which are subtypes of characters */
let s: string = "a";

s = null;
Copy the code

Compatibility between structures: compatibility between functions with fewer members: compatibility between functions with more parameters: compatibility between functions with fewer parameters

What type compatibility does:

Since TS runs we assign some variables of different types to each other. Although unreliable behavior may occur to some extent, ** increases the flexibility of the language. ** Type compatibility exists in classes, functions, and interfaces

Interface Compatibility

interface X {
  a: any;
  b: any;
}

interface Y {
  a: any;
  b: any;
  c: any;
}

let xtest: X = { a: 1.b: 2 };
let ytest: Y = { a: 1.b: 2.c: 3 };

xtest = ytest;
The attribute "c" is missing from type "X", but is required from type "Y". ts(2741)
// ytest = xtest;
Copy the code

Ytest can be copied to xtest, but xtest cannot be copied to ytest. The Y interface has all the attributes of the X interface, regardless of the additional attributes, then Y can be considered to be of type X, that is, type X is compatible with type Y —- The duck argument has fewer compatible members and more compatible members

Function compatibility

Compatibility of two functions usually occurs when two functions are assigned values to each other, such as functions as arguments

Target function, target type

Source function, source type

The target function is compatible with the source function. Three conditions must be met

Parameter limit:

The number of parameters of the target function must be greater than the number of parameters of the source function

Case 1: The number of arguments in a function is fixed
type Handler = (a: number, b: number) = > void;

function hof(handler: Handler) {
  return handler;
}
/* When passing an argument to hOF, it checks whether it is compatible with the Handler type. Handler is the target type and the argument passed to hOF is the source type

let handler1 = (a: number) = > {};
hof(handler1);

let handler2 = (a: number, b: number, c: number) = > {};
/* Parameters of type (a: number, b: number, c: number) => void cannot be assigned to parameters of type "Handler". Because the target type --Handler only has a single argument */
// hof(handler2);
Copy the code
Case 2: A function has an unfixed number of parameters

Optional arguments and remaining arguments if the function has an indefinite number of arguments

Fixed parameters are compatible with optional and residual parameters
let a = (p1: number, p2: number) = > {};
let b = (p1? :number, p2? :number) = > {};
let c = (. args:number[]) = > {};

// A is compatible with B
a = b;
// A is compatible with C
a = c;
Copy the code
Optional parameters are incompatible with fixed and residual parameters
/* Config. js "strictNullChecks": true, "strictFunctionTypes": true, */

let a = (p1: number, p2: number) = > {};
let b = (p1? :number, p2? :number) = > {};
let c = (. args:number[]) = > {};

// The type of parameter "args" and "p1" is incompatible
b = c;
// The type of parameter "p1" and "p1" are incompatible.
b = a;
Copy the code
The remaining parameters can be compatible with fixed and optional parameters
let a = (p1: number, p2: number) = > {};
let b = (p1? :number, p2? :number) = > {};
let c = (. args:number[]) = > {};

c = a;
c = b;
Copy the code

Parameter types – Parameter types must match

The base type
type Handler = (a: number, b: number) = > void;
function hof(handler: Handler) {
  return handler;
}

let handlers = (c: string) = > {};
// The types of parameter "c" and "a" are incompatible
hof(handlers);
Copy the code
Object type
interface Point3D {
  x: number;
  y: number;
  z: number;
}

interface Point2D {
  x: number;
  y: number;
}

let p3d = (points3d: Point3D) = > {};
let p2d = (points2d: Point2D) = > {};

/* Result: When the function argument is an object type, the compatibility of more members with fewer members is the opposite of the compatibility of the interface
p3d = p2d;
// Parameter "points3d" and "points2d" types are incompatible
p2d = p3d;
Copy the code

Return value type

The return value of a function of the target type must be the same as the return type of the source function, or a subtype of the return type of the source function

let f = () = > ({ name: "a" });

let g = () = > ({ name: "b".age: 20 });

// f is compatible with g
f = g;
// g is incompatible with f
g = f;
Copy the code

Because the return type of f is a subtype of the return type of G, it is also compatible with more members with fewer members (consistent with the duck argument).

Function overloading

Function overloading includes overloaded lists and function implementations. Functions in overloaded lists are target functions, and their implementations are source functions

When the program runs, it looks for a list of overloads,

In overloaded lists, the number of arguments of the target function is greater than the number of arguments of the source function

// Target function
function overloadtest(a: number, b: number, c: number) :number;
function overloadtest(a: string, b: string, c: string) :string;
/ / source function
function overloadtest(a: any, b: any) :any {}
Copy the code
function overloadtest(a: number, b: number, c: number) :number;
function overloadtest(a: string, b: string, c: string) :string;
// Error - Cause: too many parameters
function overloadtest(a: any, b: any, c: any, d: any) :any; {}Copy the code
// An error occurs - Cause: Incompatible return values
function overloadtest(a: number, b: number, c: number) :number;
function overloadtest(a: string, b: string, c: string) :string;
//
function overloadtest(a: any, b: any, c: any) {}
Copy the code

Compatibility of enumerated types

Enumeration types and numeric types can be compatible with each other

enum Fruit {
  Apple,
  Banana,
}

enum Color {
  Red,
  Yellow,
}

let fruit: Fruit.Apple = 3;

let numtest: number = Fruit.Apple;
Copy the code

Enumeration types are incompatible

enum Fruit {
  Apple,
  Banana,
}

enum Color {
  Red,
  Yellow,
}

// Can't assign type "fruit. Apple" to type "color. Red"
let color: Color.Red = Fruit.Apple;
Copy the code

Class compatibility

When comparing two classes for compatibility, static members and constructors do not participate in the comparison. If two classes have the same instance member, their instances are compatible

class A {
  constructor(p: number, q: number) {}
  id: number = 1;
}

class B {
  static s = 1;
  constructor(p: number) {}
  id: number = 2;
}

let aa = new A(1.2);

let bb = new B(1);

aa = bb;

bb = aa;
Copy the code

If a class has private members, only the parent and subclasses are compatible

class A {
  constructor(p: number, q: number) {}
  id: number = 1;
  private name: string = "";
}

class B {
  static s = 1;
  constructor(p: number) {}
  id: number = 2;
  private name: string = "";
}

let aa = new A(1.2);

let bb = new B(1);
Type "B" cannot be assigned to type "A". A type has a separate declaration of the private property "Name"
aa = bb;
Type "B" cannot be assigned to type "A". A type has a separate declaration of the private property "Name"
bb = aa;
Copy the code
class A {
  constructor(p: number, q: number) {}
  id: number = 1;
  private name: string = "";
}

class C extends A {}

let aa = new A(1.2);
let cc = new C(1.2);

aa = cc;
cc = aa;
Copy the code

Compatibility with generics

Compatibility with generic variables

// When the generic T is used by the interface, it affects the compatibility of generics
interface Empty<T> {
  value: T;
}

let objtest1: Empty<number> = {};
let objtest2: Empty<string> = {};

/ / not compatible
objtest1 = objtest2;
Copy the code

Compatibility with generic functions

If two generic functions have the same definition and no type parameters are specified, they are compatible

let log1 = <T>(x: T): T= > x;

let log2 = <U>(x: U): U= > x;

log1 = log2;

log2 = log1;
Copy the code
enum Type {
  Strong,
  Week,
}

class java {
  hellojava() {
    console.log("hello java"); }}class javascript {
  hellojavascript() {
    console.log("hello javascript"); }}function getLanguage(type: Type) {
  let lang = type === Type.Strong ? new java() : new javascript();
  / * type "Java | javascript" there is no attribute "hellojava". The attribute "helloJava" does not exist on type "javascript". ts(2339) */
  if (lang.hellojava) {
    lang.hellojava();
  } else {
    / * type "Java | javascript" there is no attribute "hellojava". The attribute "helloJava" does not exist on type "javascript". ts(2339) */
    lang.hellojavascript();
  }

  return lang;
}

getLanguage(Type.Strong);
Copy the code

Solution 1 (Type assertion)

.function getLanguage(type: Type) {
    let lang = type === Type.Strong ? new Java : new Javascript;
    if(!!!!! (langas Java).helloJava) {
        (lang as Java).helloJava();
    } else {
        (lang as Javascript).hellojavascript();
    }
    returnlang; }...Copy the code

Code readability is poor, but “type protection” can solve this problem, because it can predict the type in advance

What is type protection

Typescript can guarantee that variables are of certain types in certain blocks. Properties of this type can be carefully referenced in this block, or methods of this type can be called

Use method one (Instanceof)

.function getLanguage(type: Type) {
    let lang = type === Type.Strong ? new Java : new Javascript;
		// instanceof determines whether it belongs to a class
    if(lang instanceof Java){
        lang.helloJava();
    }else{
        lang.hellojavascript();
    }
    returnlang; }...Copy the code

Mode of use ii (in)

.function getLanguage(type: Type) {
    let lang = type === Type.Strong ? new Java : new Javascript;
		// in Determines whether it belongs to an object
    if('helloJava' in lang) {
        lang.helloJava();
    }else{
        lang.hellojavascript();
    }
    returnlang; }...Copy the code

Usage Mode 3 (Typeof)

let x: number | string = "";
// typeof determines the basic type
if (typeof x === "string") {
  // This block is of type string, which has some properties/methods of type string
  console.log(x.length);
} else if (typeof x === "number") {
  // This block is of type number, which has some properties/methods of type number
  console.log(x++);
}
Copy the code

Usage mode four (type protection function)

Create a type protection function to determine the type of an object

enum Type {
  Strong,
  Week,
}

class Java {
  helloJava() {
    console.log("hello java"); }}class Javascript {
  hellojavascript() {
    console.log("hello javascript"); }}function isJava(lang: Java | Javascript) :lang is Java {
  return (lang asJava).helloJava ! = =undefined;
}

function getLanguage(type: Type) {
  let lang = type === Type.Strong ? new Java() : new Javascript();

  if (isJava(lang)) {
    lang.helloJava();
  } else {
    lang.hellojavascript();
  }
  return lang;
}

getLanguage(Type.Strong);
Copy the code