“Don’t be afraid, don’t settle, work hard in the future” — Hello! I am little sesame 😄

Type compatibility

When one type Y can be assigned to another type X, we can say that type X is type-compatible

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

Compatibility is summed up in one sentence: Reassignment without error (automatic type conversion)

interface Named {
  name: string;
}

class Person {
  name: string;
  constructor(name: string){
    this.name = name
  }
}

let p: Named; 
p = new Person('Little Golden Sesame'); 
// Although the Person class does not explicitly say that it implements the Named interface. But it still compiles, that's compatibility
Copy the code

The basic rule of TypeScript’s structured type system is that if X is compatible with Y, then Y has at least the same properties as X. Such as:

interface Named {
  name: string;
}

let x: Named;
let y = { name: 'Alice'.location: 'Seattle' };
x = y;
Copy the code

Here we check to see if y can be assigned to x, and the compiler checks each attribute in X to see if it can find the corresponding attribute in Y. In this case, y must contain a string member whose name is name. Y satisfies this condition, so the assignment is correct.

1. Compatibility of interfaces

  • If the variable passed does not match the declared type, TS does a compatibility check
  • The principle isDuck-CheckThat is, property variables declared in the target type are compatible as long as they exist in the source type

More will do, less will not

interface X {
  a: any;
  b: any
}

interface Y {
  a: any;
  b: any;
  c: any
}
let x: X = { a: 1.b: 2 }
let y: Y = { a: 1.b: 2.c: 3 }

x = y // YES
y = x // The attribute "c" is missing from ERROR type "X", but is required from type "Y".
Copy the code

2. Function compatibility

interface Named {
  name: string;
}

let y = { name: 'Alice'.location: 'Seattle' };

function greet(n: Named) {
  console.log('Hello, ' + n.name);
}
greet(y);
Copy the code

The comparison is performed recursively, checking each member and its children.

2.1 Comparison Parameters

  • To check the compatibility of function types, to see if x can be assigned to y, first look at their argument lists.
  • Each argument to x must have a corresponding type of argument in y, notice thatIt doesn’t matter if the parameters have the same name, just their type.

You can have fewer parameters but not more

2.1.1 Fixed Parameters

let x = (a: number) = > 0;
let y = (b: number, s: string) = > 0;

y = x; // YES
x = y; // Error cannot assign type (b: number, s: string) => number to type (a: number) => number.
Copy the code

2.1.2 Optional and Remaining Parameters

StrictNullChecks: if strictNullChecks is false, the exception will be thrown instead

let a = (x: number, y: number) = > {};
let b = (x? :number, y? :number) = > {};
let c = (. args:number[]) = > {};
/ / strictNullChecks ": true
a = b // Fixed parameters are compatible with optional parameters
a = c // Fixed parameters are compatible with remaining parameters
b = c // Optional parameters are incompatible with remaining parameters
b = a // Optional parameters are not compatible with fixed parameters
c = a // The remaining parameters can be compatible with fixed parameters
c = b // The remaining parameters are compatible with optional parameters
Copy the code

StrictNullChecks “: true is not compatible with number.

2.2 Comparing Returned Values

The return value type must be a subtype of the target function return value type. (Less will not do, more will do)

let x = () = > ({name: 'Alice'});
let y = () = > ({name: 'Alice'.location: 'Seattle'});

x = y; // YES
y = x; // Error, the type "() => {name: string; } "Assign to type" () => {name: string; location: string; } ".
// type "{name: string; }" missing attribute "location", but type "{name: string; location: string; }" is required.
Copy the code

The return of y must have the location property as a string, but the return of x does not, so assigning x to y raises an exception

3. Class compatibility

  • When objects of two class types are compared, only instance members are compared. Static members and constructors are outside the scope of the comparison.
class Animal {
  feet: number;
  constructor(name: string, numFeet: number){}}class Size {
  feet: number;
  constructor(numFeet: number){}}let a: Animal;
let s: Size;

a = s;  // YES
s = a;  // YES
Copy the code
  • Private and protected members of a class affect compatibility.
    • When checking the compatibility of class instances, if the target type contains a private member, the source type must contain that private member from the same class. Similarly, this rule applies to type checking that contains instances of protected members.
    • Subclasses are allowed to assign to their parents, but not to other classes of the same type.

4. Generic compatibility

Generics make compatibility judgments about the specific type first, and then compatibility judgments

  • This is ok if the interface content is empty and you’re not using generics
interface Empty<T> {}
letx! : Empty<string>
lety! : Empty<number>
x = y // YES
Copy the code

For generic parameters that do not specify a generic type, all generic parameters are compared as any. The result type is then used for comparison

  • It is not allowed when the interface content is not empty
interface noEmpty<T> {
  data: T
}
letx! : noEmpty<string>
lety! : noEmpty<number>
x = y ERROR cannot assign type "noEmpty
      
       " to type "noEmpty
       
        ".
       
      
Copy the code

  • The implementation principle is as follows: Determine the specific type first and then determine compatibility
interface noEmptyString {
  data: string
}
interface noEmptyNumber {
  data: number 
}
// The former data is a string and the latter is a number, so it is not compatible
Copy the code

5. Compatibility with enumeration

  • Enumeration types are compatible with numeric types, and numeric types are compatible with enumeration types
  • Different enumeration types are incompatible
// Numbers can be assigned to enumerations
enum Colors {
  Red,
  Yellow,
}
let c: Colors
c = Colors.Red / / 0
c = 1 // YES
c = '1' // ERROR

// Enumeration values can be assigned to numbers
let n: number
n = 1 
n = Colors.Red // YES
Copy the code

Second, type protection

TypeScript ensures that variables are of a certain type in a particular block.

You can safely reference properties of this type in this block, or call methods of this type

enum Type { live, machinery }
class Person {
  person: string
  helloPerson() {
    console.log('Hello Person')}}class Robot {
  robot: string
  helloRobot() {
    console.log('Hello Robot')}}function getPerson(type: Type) {
  let isPerson = type === Type.live ? new Person : new Robot
  if(isPerson.helloPerson){ / / the ERROR type "Person | Robot" does not exist on the attribute "helloPerson".
    isPerson.helloPerson() / / the ERROR type "Person | Robot" does not exist on the attribute "helloPerson".
  }else{
    isPerson.helloRobot() / / the ERROR type "Person | Robot" does not exist on the attribute "helloRobot".
  }
}
getPerson(Type.live)
Copy the code

In the example above, the position of isPerson will be thrown incorrectly because we are not sure what type isPerson is. In this case, we just need to add the type assertion, and we can compile as follows:

function getPerson(type: Type) {
  let isPerson = type === Type.live ? new Person : new Robot
  if((isPerson as Person).helloPerson){ // YES
    (isPerson as Person).helloPerson() // YES
  }else{
    (isPerson as Robot).helloRobot() // YES}}Copy the code

This solved the problem, but added type assertions everywhere, adding more work and making the code less readable; Type protection is designed to solve this kind of problem

1.instanceofType of protection

  • Determines whether an instance belongs to a class
function getPerson(type: Type) {
  let isPerson = type === Type.live ? new Person : new Robot
  if(isPerson instanceof Person){
    isPerson.helloPerson()
  }else{
    isPerson.helloRobot()
  }
}
Copy the code

2,inThe operator

  • Determines whether an attribute belongs to an object
function getPerson(type: Type) {
  let isPerson = type === Type.live ? new Person : new Robot
  if('person' in isPerson){
    isPerson.helloPerson()
  }else{
    isPerson.helloRobot()
  }
}
Copy the code

3,typeofType of protection

  • Judge the base type
function double(input: string | number | boolean) {
  if (typeof input === 'string') {
    return input.toLocaleLowerCase()
  } else if (typeof input === 'number') {
    return input.toFixed(2)}else {
    return! input } }Copy the code

4. Custom type protection

  • Type protection in TS is essentially expressions that check type information at run time to make sure that a type in a scope is expected
  • To customize a type protection, you simply define a function for the type protection whose return value is a type predicate
  • The syntax for type predicates isparameterName is TypeIn this form, whereparameterNameMust be a parameter name in the current function signature
function isNumber(x: any) :x is number {
  return typeof x === "number";
}

function isString(x: any) :x is string {
  return typeof x === "string";
}

function padLeft(value: string, padding: string | number) {
  if (isNumber(padding)) {
    return Array(padding + 1).join("") + value;
  }
  if (isString(padding)) {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'. `);
}
Copy the code

5. Chain judgment operator

  • A chain judgment operator is an operator that first checks the existence of an attribute and then attempts to access it. Its symbol is? .
  • If the operand to the left of the operator? . Undefined or null, the expression evaluates to undefined otherwise, the normal trigger target attribute access, method live function call.
a? .b;// if a is null/undefined, undefined, otherwise the value of a.b is returned
=> a == null ? undefined: a.b; a? .[x];// if a is null/undefined, undefined, otherwise a[x] is returned
=> a == null ? undefined: a[x]; a? .b();// If a is null/undefined, undefined is returned
=> a == null ? undefined: a.b(); // Throw a type error if a.b is not a function, otherwise calculate the result of a.b()a? . ();// If a is null/undefined, undefined is returned
=> a == null ? undefined: a(); // Throw a type error if a is not a function, otherwise execute a()
Copy the code

reference

[1]. TypeScript Chinese

[2].typescript Tutorial