preface

  • It has been nearly a year since I wrote the project with React Family bucket + TS. I have encountered a lot of problems and doubts in the project.
  • Experience: don’t be afraid of TS, although the official documentation of TS is a lot of content, in fact, in the project commonly used are more basic things, like generic application, some advanced types of this kind of use is rare (encapsulation library, tool functions, UI components used more). As long as you familiarize yourself with common things, you can learn TS in one hour at most.
  • If this article helped you, please give it a thumbs up. Thank you

Pure TS problem

1. Changes to TS 1.5

  • Versions prior to TypeScript 1.5:moduleThe keyword can be called either “internal module” or “external module”. This can be a bit confusing for developers new to TypeScript.
  • Version 1.5 of TypeScript: The terminology has changed so that the concept of “internal modules” is closer to what most people think of as “namespaces” and hence “namespaces” (i.e., module X {… } is equivalent to the currently recommended namespace X {… }), and “external module” is a module to JS (ES6 module system treats each file as a module), so it is shortened to “module” thereafter.
  • Namespaces are not recommended

before

module Math {
    export function add(x, y) {... }}Copy the code

after

namespace Math {
    export function add(x, y) {... }}Copy the code

2. Null and undefined are subtypes of other types (including void) and can be assigned to other types (e.g. numeric types)

  • By default, the compiler prompts an error because tsconfig.json has a configuration item that is enabled by default.
// tsconfig.json 

{
 	 /* Strict Type-Checking Options */
    "strict": true./* Enable all strict type-checking options. */
    // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
    // Check for null types. If set to false, no errors are reported
    // "strictNullChecks": true, /* Enable strict null checks. */
    // "strictFunctionTypes": true, /* Enable strict checking of function types. */
    // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
}
Copy the code
  • StrictNullChecks the strictNullChecks parameter is used in the new strictNullChecks mode. In strictNullChecks mode, null and undefined are not of either type. They can only be assigned to their own type or to any

3. Difference between never and void

  • Void means that there is no type (which can be assigned to null and undefined).
  • Never represents a type that does not contain a value, that is, a value that never exists.
  • Functions that have a void return value type work fine. A function that returns a value of type never will not return properly, will not terminate, or will throw an exception.

4. The transgression of Yuanzu

let aaa: [string.number] = ['aaa'.5];
// No error will be reported when adding
aaa.push(6);
// Print the entire ancestor without error
console.log(aaa); / / [' aaa ', 5, 6].
// An error occurs when printing added elements
console.log(aaa[2]); // error
Copy the code

5. Characteristics of enumerators

  • The property is read-only and cannot be modified
  • Enumerator values are incremented from 0 by default, and you can customize the initial value
enum Gender {
    BOY = 1,
    GRIL
}
console.log(Gender.BOY);/ / 1
console.log(Gender);// { '1': 'BOY', '2': 'GRIL', BOY: 1, GRIL: 2 }
Copy the code
  • Enumerator value
    • You can have no initial value
    • Can be a reference to a constant member
    • It can be a constant expression
    • It can also be a nonconstant expression
enum Char {
    // const member: the result is computed at compile time
    a,				 // No initial value
    b = Char.a,// A reference to a constant member
    c = 1 + 3.// Constant expression
  
    // computed Member Computes members: Expressions are reserved until the execution phase of the program
    d = Math.random(),// nonconstant expression
    e = '123'.length,
    // The enumerator immediately after the calculated member must have an initial value
    f = 6,
    g
}
Copy the code

6. Differences between constant enumerations and ordinary enumerations

  • Constant enumerations are removed at compile time
  • Enumeration members can only be constant members
const enum Colors {
    Red,
    Yellow,
    Blue
}
// Constant enumerations are removed at compile time
let myColors = [Colors.Red, Colors.Yellow, Colors.Blue];
Copy the code

Compiled into JS

"use strict";
var myColors = [0 /* Red */.1 /* Yellow */.2 /* Blue */];
Copy the code
  • Constant enumerationCannot contain computed members ifContains computed members, an error is reported at compile time
/ / an error
const enum Color {Red, Yellow, Blue = "blue".length};
console.log(Colors.RED);
Copy the code

7. Usage scenarios of enumeration

Problems with the following code:

  • Poor readability: Difficulty remembering the meaning of numbers
  • Poor maintainability: hard coding, subsequent modification of the lead to launch the whole body
function initByRole(role) {
    if (role === 1 || role == 2) {
        console.log("1, 2,")}else if (role == 3 || role == 4) {
        console.log('3, 4')}else if (role === 5) {
        console.log('5')}else {
        console.log(' ')}}Copy the code

After using enumeration

enum Role {
  Reporter,
  Developer,
  Maintainer,
  Owner,
  Guest
}

function init(role: number) {
  switch (role) {
    case Role.Reporter:
      console.log("Reporter:1");
      break;
    case Role.Developer:
      console.log("Developer:2");
      break;
    case Role.Maintainer:
      console.log("Maintainer:3");
      break;
    case Role.Owner:
      console.log("Owner:4");
      break;
    default:
      console.log("Guest:5");
      break;
  }
}

init(Role.Developer);
Copy the code

8. What is an indexable interface

  • Generally used to constrain arrays and objects
// Numeric index -- constraint array
// index is an arbitrary name
// As long as index is of type number, the value must be of type string
interface StringArray {
  // The key is of type number and usually represents an array
  // Restrict value to string
  [index:number] :string
}
let arr:StringArray = ['aaa'.'bbb'];
console.log(arr);


// String index -- constraint object
// As long as index is of type string, the value must be of type string
interface StringObject {
  // Key is of type string, which generally represents an object
  // Restrict value to string
  [index:string] :string
}
let obj:StringObject = {name:'ccc'};
Copy the code

9. What is a function-type interface

  • Constrain the parameters and return values passed to the method
// Notice the difference

// Common interface
interface discount1{
  getNum : (price:number) = > number
}

// Function type interface
interface discount2{
  / / note:
  // The signature before the ":" is used to constrain the parameters of the function
  // ":" is used to constrain the return value of the function
  (price:number) :number
}
let cost:discount2 = function(price:number) :number{
   return price * 8.;
}

// Type aliases can also be used
type Add = (x: number, y: number) = > number
let add: Add = (a: number, b: number) = > a + b
Copy the code

10. What is a class interface

  • If the interface is used for a class, then the interface represents “behavior abstraction”.
  • Constraints on classes to implement interfaces, classes can implement multiple interfaces
  • Interfaces can constrain only public members of a class (instance properties/methods), not private members, constructors, or static properties/methods
// Interfaces can be represented in object-oriented programming as an abstraction of behavior
interface Speakable {
    name: string;
  
 		// ":" is the signature of the function, which is used to constrain the parameters of the function
    // ":" is used to constrain the return value of the function
    speak(words: string) :void
}

interface Speakable2 {
    age: number;
}

class Dog implementsSpeakable, Speakable2 { name! :string;
    age = 18;

    speak(words: string) {
        console.log(words); }}let dog = new Dog();
dog.speak(Woof woof woof);
Copy the code

11. What is a mixed-type interface

  • An object can be used as both a function and an object
interface FnType {
    (getName:string) :string;
}

interface MixedType extends FnType{
    name:string;
    age:number;
}
Copy the code
interface Counter {
    (start: number) :string;
    interval: number;
    reset(): void;
}

function getCounter() :Counter {
    let counter = <Counter>function (start: number) {}; counter.interval =123;
    counter.reset = function () {};return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
Copy the code

12. What is function overloading

  • In Java, function overloading refers to two or more functions of the same name with different types or numbers of arguments. The advantage of function overloading is that you do not need to have different names for functions that have similar functions.
  • In TypeScript, this means providing multiple function type definitions for the same function, suitable for situations that accept different arguments and return different results.
  • When TS implements function overloading, it requires that a series of function declarations be defined to implement overloading in the broadest version of the type. The final function implementation is usually of type any.
  • Function overloading is rarely used in practice and is usually replaced by associative or generic types
  • Declarations of function overloading are used only in the type checking phase and are removed after compilation
  • When dealing with overloads, the TS compiler queries the list of function claims from top to bottom until a match is found, so the easiest type to match is first
function attr(val: string) :string;
function attr(val: number) :number;
// The first two lines are function declarations, and this line implements function overloading
function attr(val: any) :any {
    if (typeof val === 'string') {
        return val;
    } else if (typeof val === 'number') {
        return val;
    } 
}

attr('aaa');
attr(Awesome!);
Copy the code
  • After the function is declared, the function must be overloaded. You can also just declare functions.
// The function declaration in the interface written after has a higher priority
interface Cloner111 {
    clone(animal: Animal): Animal;
}
interface Cloner111 {
    clone(animal: Sheep): Sheep;
}
interface Cloner111 {
    clone(animal: Dog): Dog;
    clone(animal: Cat): Cat;
}

// ==> The interface with the same name will be merged
// The function declaration in the interface written after has a higher priority
interface Cloner111 {
    clone(animal: Dog): Dog;
    clone(animal: Cat): Cat;
    clone(animal: Sheep): Sheep;
    clone(animal: Animal): Animal;
}


interface Cloner222 {
		// The interface is sorted in writing order. The interface written first has a higher priority
    clone(animal: Dog): Dog;
    clone(animal: Cat): Cat;
    clone(animal: Sheep): Sheep;
    clone(animal: Animal): Animal;
}
Copy the code

13. What are access control modifiers

class Father {
    str: string; // The default is public
    public name: string;   // Within the defined class, instances of the class, subclasses, and subclass instances can be accessed
    protected age: number; // Can only be accessed in defined classes and subclasses, not through instances (instances of defined classes and subclasses)
    private money: number; // Can only be accessed in the defined class. Instances, subclasses, and subclass instances are not accessible
    constructor(name: string, age: number, money: number) {
        this.name = name;
        this.age = age;
        this.money = money;
    }

    getName(): string {
        return this.name;
    }

    setName(name: string) :void {
        this.name = name; }}const fa = new Father('aaa'.18.1000);
console.log(fa.name);// aaa
console.log(fa.age);// error
console.log(fa.money);// error

class Child extends Father {
    constructor(name: string, age: number, money: number) {
        super(name, age, money);
    }

    desc() {
        console.log(`${this.name} ${this.age} ${this.money}`); }}let child = new Child('bbb'.18.1000);
console.log(child.name);// bbb
console.log(child.age);// error
console.log(child.money);// error
Copy the code

14. Override vs overload

  • Overrides are when a subclass overrides a method that “inherits” from a parent class. Although TS is similar to JAVA, inheritance in TS is essentially JS’s “inheritance” mechanism – the prototype chain mechanism
  • Overloading means providing multiple type definitions for the same function
Class Animal {speak(word: string): string {return 'action :' + word; }} class Cat extends Animal {speak(word: string): string {return 'Cat :' + word; } } let cat = new Cat(); console.log(cat.speak('hello')); /**--------------------------------------------**/ function double(val: number): number function double(val: string): string function double(val: any): any { if (typeof val == 'number') { return val * 2; } return val + val; } let r = double(1); console.log(r);Copy the code

15. Inheritance vs. polymorphism

  • Inheritance: A subclass inherits its parent class, which has all the features of its parent class and some more specific features
  • Polymorphisms: Different classes that are related by inheritance can have different responses to the same method
class Animal {
    speak(word: string) :string {
        return 'Animal: '+ word; }}class Cat extends Animal {
    speak(word: string) :string {
        return 'Cat:'+ word; }}class Dog extends Animal {
    speak(word: string) :string {
        return 'Dog:'+ word; }}let cat = new Cat();
console.log(cat.speak('hello'));
let dog = new Dog();
console.log(dog.speak('hello'));
Copy the code

16. What are generics

  • Generics are the feature of defining functions, interfaces, or classes without specifying a specific type in advance.
  • You can think of generics as parameters that represent types
// What type of value do we want passed in and returned
// The value passed in can be of any type, so generics can be used

If you use any, you lose the value of type checking
function createArray1(length: any, value: any) :Array<any> {
    let result: any = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

let result = createArray1(3.'x');
console.log(result);

// The dumbest way to write it is to define a function for each type
function createArray2(length: number, value: string) :Array<string> {
    let result: Array<string> = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

function createArray3(length: number, value: number) :Array<number> {
    let result: Array<number> = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

// Or use function overloading, which is a bit cumbersome to write
function createArray4(length: number, value: number) :Array<number>
function createArray4(length: number, value: string) :Array<string>
function createArray4(length: number, value: any) :Array<any> {
    let result: Array<number> = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray4(6.'666');
Copy the code

Use generic


       
       
      
function createArray<T> (length: number, value: T) :Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

// Specify the type when used
let result = createArray<string> (3.'x');

// You can also specify no type, TS will automatically derive the type
let result2 = createArray(3.'x');
console.log(result);
Copy the code

17. What are type predicates

  • Type protection function: To customize a type protection, you simply define a function for the type protection whose return value is a type predicate
  • Type predicatesThe syntax forparameterName is TypeIn this form, whereparameterNameMust be a parameter name in the current function signature
interface Bird {
    fly()
    layEggs()
}
interface Fish {
    swim()
    layEggs()
}

function getSmallPet() :Fish | Bird{
    return ;
}
let pet = getSmallPet();

pet.layEggs();
// When using federated types, by default only the common part is retrieved from them without type assertion
(pet as Fish).swim();
pet.swim();
Copy the code

interface Bird {
    fly()
    layEggs()
}
interface Fish {
    swim()
    layEggs()
}

function getSmallPet() :Fish | Bird{
    return ;
}
let pet = getSmallPet();

// Use type predicates
function isFish(pet:Fish | Bird) :pet is Fish {
    return (pet asFish).swim ! = =undefined;
}

if(isFish(pet)){
    pet.swim();
}else{
    pet.fly();
}
Copy the code

18. Use of optional chain operators

  • Optional chainOperator is an operator that checks for the existence of an attribute and then attempts to access it. The symbol is? .
  • If the operand to the left of the operator? .Evaluates to undefined or null, then the expression evaluates to undefined. Otherwise, target property access, method, or function calls are normally triggered.
  • The optional chain operator is in the STAGE3 stage and is used@babel/plugin-proposal-optional-chainingThe plugin can be used in advance. TS 3.7 is officially supported
a? .b;// a == null? undefined : a.b;
// if a is null/undefined, undefined, otherwise the value of a.b is returned.a? .[x];// a == null? undefined : a[x];
// if a is null/undefined, undefined, otherwise a[x] is returneda? .b();// a == null? undefined : a.b();
// If a is null/undefined, undefined is returned
Type error exceptions are thrown if a.b is not a function, otherwise the result of a.b() is computed
Copy the code

19. Use of non-null predictors

  • TS 3.7 is officially supported
let root: any = document.getElementById('root');
root.style.color = 'red';

let root2: (HTMLElement | null) = document.getElementById('root');
// The non-empty assertion operator --> is written to fool the compiler and prevent compile-time errors, which may still occur in packaged coderoot2! .style.color ='red';
Copy the code

20. Use of null-value merge operators

  • TS 3.7 is officially supported
  • ||Disadvantages of operators:Is treated as if the result of the expression on the left is a numeric 0 or an empty stringfalse.
  • Null-value merge operator: returns the result of the right expression only if the result of the left expression is null or undefined. In this way, the values of undefined, null, and false can be clearly distinguished.
const data = {
    str:' '.// num:0,
    flag:false.// flag: null,
};

// data. STR is ""
let str1 = data.str || 'empty' / / 'empty'
// data.num is 0
let num1 =  data.num || Awesome! / / 666
// data.flag is false
let status1 =  data.flag || true  // true


// data. if STR is "", it can pass. It cannot pass only when STR is undefined or null
let st2r = data.str ?? 'empty';  
// Data. num is 0. This parameter cannot be passed only when num is undefined or null
let num2 = data.num ?? Awesome!; 
// Data. flag is false. Only when flag is undefined or null, the flag cannot pass
let status2 = data.flag ?? true;

console.log('str=>', str2);
console.log('num=>', num2);
console.log('status=>', status2);
Copy the code

21. What is the difference between typeof class and class directly

class Greeter {
    static message = 'hello';

    greet(){
        returnGreeter.message; }}// Gets the instance's type, which can get properties/methods on the instance object
let greeter1:Greeter = new Greeter();
console.log(greeter1.greet());// 'hello'


// Gets the type of the class that can get static properties/methods on the class
let greeterTwo:typeof Greeter = Greeter;
greeterTwo.message = 'hey';

let greeter2:Greeter = new greeterTwo();
console.log(greeter2.greet());// 'hey'
Copy the code

22. What exactly is the use of the never type in TS?

23. When using federated types, by default only the common part is fetched from them in the case of an undefined type

  • Using type assertions
interface Bird {
    fly()
    layEggs()
}
interface Fish {
    swim()
    layEggs()
}

function getSmallPet() :Fish | Bird{
    return ;
}

let pet = getSmallPet();
pet.layEggs();
// When using federated types, by default only the common portion is retrieved from them in the case of an undefined type
// Type assertion is required
(pet as Fish).swim();
pet.swim();
Copy the code

  • Distinguishable union types (with the help of never)
enum KindType{
    square = 'square',
    rectangle = 'rectangle',
    circle = 'circle',}interface Square {
    kind: KindType.square;
    size: number;
}

interface Rectangle {
    kind: KindType.rectangle;
    width: number;
    height: number;
}

interface Circle {
    kind: KindType.circle;
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area1(s: Shape) {
    // If multiple types in a federated type have a common attribute, then different type-protected blocks can be created with this attribute
    // where kind is the common attribute
    switch (s.kind) {
        case KindType.square:
            return s.size * s.size;
        case KindType.rectangle:
            return s.height * s.width;
        default:
            return; }}// The above code has hidden dangers. If the new type is missing, no error will be reported when TS checks the above code
console.log(area1({kind: KindType.circle, radius: 1}));


function area2(s: Shape) {
    switch (s.kind) {
        case KindType.square:
            return s.size * s.size;
        case KindType.rectangle:
            return s.height * s.width;
        case KindType.circle:
            return Math.PI * s.radius ** 2;
        default:
            // Check whether s is of type never
            // If it is of type never, the above branches are overridden and will never reach the current branch
            // If it is not of type never. It indicates that the previous branch statement is missing and needs to be filled
            return ((e: never) = > {throw new Error(e)}) (s)}}console.log(area2({kind: KindType.circle, radius: 1}));
Copy the code

24. What ‘s on the version 3.3.3333?

25. Some variables cannot be typed in a global environment

let name: string;

// If export is added, no error will be reported
// export {} 
Copy the code

26. Unnecessary namespaces: Namespaces and modules should not be used together. Namespaces should not be used in a module, but in a global environment

You might write something like: Export the namespace

  • shapes.ts
export namespace Shapes {
    export class Triangle { / *... * / }
    export class Square { / *... * /}}Copy the code
  • shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle();
Copy the code

Should not be used in the module’s namespace or exported namespace: use namespace is in order to provide a logical grouping and avoid naming conflicts, module file itself is a logical grouping, and its name is specified by importing the module code, so there’s no need for the export of object additional module layer.

Here are some examples of improvements:

  • shapes.ts
export class Triangle { / *... * / }
export class Square { / *... * / }
Copy the code
  • shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Triangle();
Copy the code

or

  • shapes.ts
 namespace Shapes {
    export class Triangle { / *... * / }
    export class Square { / *... * /}}Copy the code


  • shapeConsumer.ts
let t = new Shapes.Triangle();
Copy the code

27. Expand the types of global variables

interface String {
    // This is an extension, not an overlay, so feel free to use it
    double(): string;
}

String.prototype.double = function () {
    return this + '+' + this;
};
console.log('hello'.double());

// If this is added, an error will be reported
// export {}
Copy the code
interface Window {
    myname: string
}

// Note: the window should be lowercase
console.log(window);

// If this is added, the current module becomes local
// Then the type Window is a local variable, not a global variable
// So the Window extension/method is invalid
export {}
Copy the code

28. export = xxx 和 import xxx = require(‘xxx’)

  • Both the CommonJS and AMD environments have an exports variable that contains all the exported content of a module. Both CommonJS and AMD exports can be assigned to an object, in which case it acts like the export default in the ES6 syntax. The export Default syntax is not compatible with CommonJS and AMD exports, although it has similar functions.
  • If a module complies with the ES6 module specification, when exporting content by default (export default XXX), the ES6 module system automatically adds a default attribute to the top-level object of the current module, pointing to the exported content. When an ES6 module introduces this module (import moduleName from ‘XXX’), the ES6 module system automatically looks for the default attribute on the top-level object in the module and assigns the value to moduleName by default. Var moduleName = require(‘ XXX ‘)) if the ES6 module is used directly by a non-ES6 module (var moduleName = require(‘ XXX ‘)), an error will be reported.
  • To support CommonJS and AMD exports, TypeScript provides export = syntax. Export = Syntax defines the export object of a module. The term object here refers to a class, interface, namespace, function, or enumeration. To export a module using export =, you must import the module using TypeScript’s special syntax import Module = require(“module”).
// exports === module.exports // these two variables share the same memory address

// Export as a whole
// module.exports = {}

// Export multiple variables
exports.c = 3;
exports.d = 4;
Copy the code
  • An ES6 module is exported by default and used by a Node module
// Compatibility only works in TS !!!!!!
// Compatibility only works in TS !!!!!!
// Compatibility only works in TS !!!!!!

// a.es6.ts
// Only one can be exported
export = function () {
    console.log("I'm default")}// b.node.ts
import fn = require('./a.es6.ts');
fn();
Copy the code

29. How do I use TS in Node

  • Install related declaration files such as @types/node;
  • Because node modules follow the CommonJS specification, some node module declarations (such as Express) use export = XXX to export module declarations. When TS deduces the type, it fails to infer and an error is reported. Import XXX from “XXX” or import XXX = “XXX” into the node module.

Use as instead of Angle brackets for type assertions

  • Angle brackets can be used in TS to indicate type assertions, but this presents parsing difficulties when combined with JSX’s syntax. So T sub s is.tsxType assertions using Angle brackets are disabled in the file.
  • asThe operator in.tsFiles and.tsxIt’s all available in the file
interface Person {
    name: string;
    age: number
}

let p1 = {age: 18} as Person;
console.log(p1.name);

// This will cause errors in.tsx files
let p2 = <Person>{age: 18};
console.log(p2.name);
Copy the code

31. How to type check JS files

  • This can be set in tsconfig.jsoncheckJs:truefor.jsFile type check and error prompt.
    • Through the.jsFile top add// @ts-nocheckComment to let the compiler ignore type checking for the current file.
    • Instead, you can do this by not settingcheckJs:trueAnd in the.jsAdd one at the top of the file// @ts-checkComment to allow the compiler to examine the current file.
    • You can also configure include/exclude in tsconfig.json to select /exclude certain files from type checking.
    • You can still use it// @ts-ignoreTo ignore errors in the line.
  • in.jsIn a file, the type can be matched in.tsThe same thing was deduced in the file. Can pass when the type cannot be inferredJSDocTo specify the type.
/** @type {number} */
var x;

x = 0;      // OK
x = false;  // Error: boolean is not assignable to number
Copy the code
  • JSDoc annotations supported in TS

32. Do not use the following types: Number, String, Boolean, Object. Use the types Number, String, Boolean, Object

/ * error * /
function reverse(s: String) :String;

/* OK */
function reverse(s: string) :string;
Copy the code

33. How to deconstruct a functionfunction fn({ x: number }) { /* ... */ }When, you can declare the type of the variable and set the default value for the variable

// error
function f({ x: number }) {
    console.log(x);
}

// ok
function f({x}: { x: number } = {x: 0}) {
    console.log(x);
}
Copy the code

The result returned by Pick is an object (or new interface) that contains the picked properties

interface Test {
    arr: string[]}// pick the returned result => {arr: string[]}
let aaa: Pick<Test, 'arr'> = {arr: ['1']};
Copy the code

35. Cannot traverse map data using for of

const map = new Map([['F'.'no'],
  ['T'.'yes']]);for (let key of map.keys()) {
  console.log(key);
}

// forEach can also be traversed
map.forEach((value,key) = > {
 console.log(key);
});
Copy the code
  • An error is reported when setting target=es5 and the for statement cannot be executed

TS2569: Type ‘Map<string, string>’ is not an array type or a string type. Use compiler. option ‘- downlevellteration’ to allow iterating of iterators.

Set dom. Iterable and downlevelIteration to run tsconfig.json

{
	/* Full support for iterators in for-of, extension operators, and destruct assignments when targeting ES5 or ES3 */
	"downlevelIteration": true."lib": [
    "dom"."es5"."es6"."es7"."dom.iterable"]}Copy the code
  • When target=es6 is set, this will work. The reason:

Note: If –lib is not specified, the default library list is injected. Into the default library is: excellent For — target ES5: DOM, ES5, ScriptHost For the For – target ES6: DOM, ES6, DOM. Iterable, ScriptHost

36. Sometimes we need to reuse a type, but we don’t need all the attributes in that type, so we need to cull some attributes

  • This method is often used in React. When the parent component passes data down through props, the props type of the parent component needs to be reused, but some useless types need to be removed.
interface User {
    username: string
    id: number
    token: string
    avatar: string
    role: string
}
type UserWithoutToken = Omit<User, 'token'>
Copy the code

37. Why is a module in exclude still used by the compiler

Json is sometimes added automatically. If the compiler recognizes a file as a module import target, it will be added to the compiled list, whether it is excluded or not. Therefore, to exclude a file from the compiled list, you need to exclude it as well as all files that import it or use the /// directive.

38. Use import XXX = namespace. XXX to create a namespace alias

// a.ts
namespace Shape {
    const pi = Math.PI;

    export function cricle(r: number) {
        return pi * r ** 2}}// b.ts
// Use it directly
// console.log(Shape.cricle(2));

// Or use variables/functions/classes in the namespace in the following way
// import newName = A.B.C.D to give a short name to a common, deep-level object
// The import function here is to create an alias for any identifier, including objects in the imported module
// Don't be confused with the import x from "module-name" syntax used to load modules
import cricle = Shape.cricle;
console.log(cricle(2));  
Copy the code
  • Notice that it is not used hererequireKeyword, but assign directly using the qualified name of the import symbol. With the usevarSimilar, but it also applies to types and imported symbols with namespace meaning. And the important thing is, in terms of values,importWill generate a different reference from the original symbol, so change the aliasvarValue does not affect the value of the original variable.

Tsconfig. json Comments about common configuration items

{
    "compilerOptions": {

        /************** Basic configuration **************/
        /************** Basic configuration **************/
        /************** Basic configuration **************/

        /* Enable incremental compilation: The TS compiler generates a file to store the compilation information during the first compilation, and increments the compilation from this file during the next compilation to improve the compilation speed */
        // "incremental": true,
        /* Specifies the file location where incremental compilation information is stored */
        // "tsBuildInfoFile": "./",

        /* Prints diagnostic information */
        // "diagnostics": true,
        /* Print out the file */
        // "listEmittedFiles": true,
        /* Prints the compiled file (including the referenced declaration file) */
        // "listFiles": true,

        /* Specify the target version of ECMAScript: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
        // "target": "es5",
        / * of the specified module code generation: 'none', 'commonjs',' amd ', 'the system', 'umd', 'es2015', or 'ESNext. * /
        // "module": "commonjs",

        /* Specifies the library file to be included in the compilation -- reference the class library -- that is, the declaration file. If the output module is ES5," DOM ","es5","scripthost" */ will be introduced by default
        /* If you want to use some of the ES6 + syntax in TS, you need to introduce the related class library */
        // "lib": [],

        /* Allows you to compile JS files */
        // "allowJs": true,
        /* Check the JS file */
        // "checkJs": true,

        /* Specify JSX code generation modes: 'preserve', 'react-native', or 'react'. */
        /* In 'React' mode: TS directly compiles JSX to js */
        /* In 'Preserve' mode: TS does not compile JSX to JS, but keeps JSX */
        // "jsx": "preserve",


        /************** Declaration file configuration **************/
        /************** Declaration file configuration **************/
        /************** Declaration file configuration **************/

        /* Generate the corresponding type declaration file -- '.d.ts' */
        // "declaration": true,
        /* Declare the output path of the file */
        // "declarationDir": "./d",
        /* Only declaration files are generated, JS */ is not generated
        // "emitDeclarationOnly": true,
        /* Declare the file directory, default node_modules/@types */
        // "typeRoots": [],
        /* The package of declaration files to import. By default, all declaration files in the directory above are imported */
        // "types": [],


        * "module":" AMD ", when one module introduces another module, the compilation results of the two modules are merged into one file */
        // "outFile": "./",
        /* Specifies the output directory of the compiled file */
        // "outDir": "./out",
        /* Specifies the root directory of the input file, which controls the structure of the output directory */
        // "rootDir": "./",

        /* Enable project compilation */
        // "composite": true,

        /* Remove comments */ while output
        // "removeComments": true,

        /* No output file */
        // "noEmit": true,
        /* No output file */ when an error occurs
        // "noEmitOnError": true,

        If the helper function is set to true, you need to install the ts-helpers */
        /* Similar to Babel, helper functions are generated for each file, which makes the final compiled package larger */
        // "noEmitHelpers": true,
        /* It is now possible to introduce helper functions via tslib (TS built-in library). File must be module!! * /
        Var tslib_1 = require("tslib") */
        // "importHelpers": true,

        /* Full support for iterators in for-of, extension operators, and destruct assignments when targeting ES5 or ES3 */
        // "downlevelIteration": true,

        /* Convert each file into a separate module */
        // "isolatedModules": true,


        /************** Strictly check the configuration **************/
        /************** Strictly check the configuration **************/
        /************** Strictly check the configuration **************/

        /* Enable all strict check configurations */
        "strict": true./* The implicit any type */ is not allowed
        // "noImplicitAny": true,

        /* It is not allowed to assign null and undefined to other types of variables */
        // "strictNullChecks": true,

        /* Bidirectional covariance of function arguments is not allowed */
        // "strictFunctionTypes": true,

        /* When using bind/call/apply, strictly check the function parameter types */
        // "strictBindCallApply": true,

        /* Class instance attributes must be initialized */
        // "strictPropertyInitialization": true,

        /* This is not allowed to have an implicit any type, i.e. this must have an explicit reference to */
        // "noImplicitThis": true,

        /* Parses in strict mode and injects "use strict" */ into each source file
        // "alwaysStrict": true,

        / * * * * * * * * * * * * * * additional syntax checking configuration, this kind of check to eslint line, it is not necessary to configure * * * * * * * * * * * * * * /
        / * * * * * * * * * * * * * * additional syntax checking configuration, this kind of check to eslint line, it is not necessary to configure * * * * * * * * * * * * * * /
        / * * * * * * * * * * * * * * additional syntax checking configuration, this kind of check to eslint line, it is not necessary to configure * * * * * * * * * * * * * * /

        /* An error was reported with an unused local variable */
        // "noUnusedLocals": true,

        /* An error was reported with an unused function argument */
        // "noUnusedParameters": true,

        /* Each branch must have a return value */
        // "noImplicitReturns": true,

        /* Verify switch-case syntax */
        // "noFallthroughCasesInSwitch": true,

        /************** Module resolution configuration **************/
        /************** Module resolution configuration **************/
        /************** Module resolution configuration **************/

        /* Specify the parsing strategy for the module: 'node' (node.js) or 'classic' (TypeScript pre-1.6)*/
        / * if not specified, then in use, the module of AMD | System | ES2015 when the default value is Classic and other situations for Node * /
        // "moduleResolution": "node",

        /* The base path */ when resolving non-absolute path module names
        // "baseUrl": "./",

        /* Set of path maps based on 'baseUrl' */
        // "paths": {},

        /* Puts multiple directories in a single virtual directory for runtime */
        /* When the library and the development code are both output to the same directory, the development code and the library location is not the same, the development code into the library path is not correct */
        // "rootDirs": [],
        // "rootDirs": ["src","out"],

        /* Allow export = XXX to be exported and import */ using import XXX form "module-name"
        // "esModuleInterop": true,

        /* Modules that do not export by default can be imported by other modules by default. This has no effect on code execution, but only on type checking */
        // "allowSyntheticDefaultImports": true,


        /* Do not parse the real path of Symlinks */
        // "preserveSymlinks": true,

        /* Allows access to UMD module contents in modules as global variables */
        // "allowUmdGlobalAccess": true,


        /************** Source Map configuration **************/
        /************** Source Map configuration **************/
        /************** Source Map configuration **************/

        /* Specifies the ts file location */
        // "sourceRoot": "",

        /* Specify the location of the map file */
        // "mapRoot": "",

        /* Generate the sourceMap */ for the target file
        // "sourceMap": true,

        /* Generate code and sourcemaps in a file, requiring that either the --inlineSourceMap or --sourceMap attributes */ are set
        // "inlineSources": true,

        /* Generate an inline sourceMap for the target file -- the source file and sourceMap file are in the same file instead of putting the map file in a separate file */
        // "inlineSourceMap": true,

        /* Generate the sourceMap */ for the declaration file
        // "declarationMap": true,

        /************** Experimental configuration **************/
        /************** Experimental configuration **************/
        /************** Experimental configuration **************/

        /* Enable decorator */
        // "experimentalDecorators": true,

        // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */


        /************** Advanced configuration **************/
        /************** Advanced configuration **************/
        /************** Advanced configuration **************/

        /* Enforces case sensitivity */
        // "forceConsistentCasingInFileNames": true

}

    /* Specifies a list of individual files to compile */
    // "files": [],

    /* Specify the file/directory to compile */
    // "include": [
    // // is equivalent to "./ SRC /**/*"
    // "src"
    / /]

    /* Files or directories to exclude */
    // "exclude": []

    /* Config file inheritance */
    // "extends": "./tsconfig.base.json"

}
Copy the code

The tsconfig.json configuration item is faulty

1. Three JSX modes

  • To use JSX in TS, you must do two things:
    1. Give the file a.tsx extension
    2. Enable the JSX option
  • TS has three JSX modes: Preserve, React, and React-Native. These modes only work in the code generation phase and type checking is not affected.
    • In Preserve mode: JSX is not compiled to JS, JSX is preserved in the generated code for subsequent conversion operations (e.g., Babel). In addition, the output file will have the.jsx extension.
    • In React mode: When JSX is directly compiled to JS, the react. CreateElement format is generated. Conversion operations are not required before use.
    • In React-native mode: equivalent to preserve, it also preserves all JSX, but the output file has a.js extension.
model The input The output Output file extension
preserve <div /> <div /> .jsx
react <div /> React.createElement("div") .js
react-native <div /> <div /> .js

2. Pay attention to the “lib” configuration item

  • When you installTypeScriptWill be installed along withlib.d.tsSuch declaration file, this fileIncludes JavaScript runtime and various common environment declarations in the DOM.
    • It is automatically included in the compilation context of a TypeScript project
    • It allows you to quickly start writing type-checked JavaScript code
  • tsconfig.jsonThe lib option is used to specify which declaration library files need to be injected for the current project. If not specified, the default list of injected library files is:
    • when--target ES5:DOM,ES5,ScriptHost
    • when--target ES6:DOM,ES6,DOM.Iterable,ScriptHost
  • If you want to use some version of ES6 or higher or special syntax in TS, you need to introduce the relevant class libraries. Such as:ES7DOM.Iterable

3. “moduleResolution” resolution policy

www.tslang.cn/docs/handbo…

4. When target is set to ES6, TSC will use the “classic” module by defaultimport * as abc from "@babel/types"This non-relative path import cannot be resolved correctly.

  • Solution: Specify the resolution policy as node => “moduleResolution”: “node”.

5. What is the role of “esModuleInterop”

  • If a module complies with the ES6 module specification, when exporting content by default (export default XXX), the ES6 module system automatically adds a default attribute to the top-level object of the current module, pointing to the exported content. When an ES6 module introduces this module (import moduleName from ‘XXX’), the ES6 module system automatically looks for the default attribute on the top-level object in the module and assigns the value to moduleName by default. Var moduleName = require(‘ XXX ‘)) if an ES6 module is used by a non-ES6 module (var moduleName = require(‘ XXX ‘)), an error will be reported.
  • TypeScript introduces the esModuleInterop option for compatibility, which sets esModuleInterop to true and automatically adds the default attribute to the module at compile time. Instead of importing moduleName = require(‘ XXX ‘), non-ES6 modules can be imported using the import moduleName from ‘XXX’ form.

6. What your role is “allowSyntheticDefaultImports”

  • Allow default import of modules that have not been set to export default XXX. You can import modules as import XXX from ‘XXX’
/ / configuration before
import * as React from 'react';
import * as ReactDOM from 'react-dom';

/ / configuration
import React from 'react';
import ReactDOM from 'react-dom';
Copy the code

7. “Paths” Points for attention when configuring the path mapping set

{
   "paths": {
      // The path must be followed by "/*"
      "@public/*": [
        // The path must be followed by "/*"
        "public/*"]."@src/*": [
        "src/*"]."@assets/*": ["src/assets/*"]."@components/*": [
        "src/components/*"]}}Copy the code

8. Problems with “allowJs

  • Set “allowJs”: false: there will be no prompt when.js /.jsx files are introduced in.ts /.tsx files

React + TS project issues

1. Using import to introduce non-JS modules will cause an error, while using require is fine

import styles from './login.less';
import logo from '@assets/images/logo.svg';

const logo2 = require('@assets/images/logo.svg');
console.log(logo2);// path
Copy the code

Workaround: Add declarations to these non-JS modules

/** * style */
declare module '*.css'
declare module '*.less'
// declare module "*.less" {
// const styles: { [className: string]: string };
// export default styles
// }
declare module'*.scss' /** * image */ DECLAREmodule '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
Copy the code

Import * as React from ‘React’ and import React from ‘React’

  • The first option is to assign all the members exported from export to React. After importing React, use React
  • The second method simply assigns the export default to React

3. Resolve the odd introduction of import * as XXX from ‘XXX’

  • Configuration tsconfig. Json
{/ / allow the default import did not set the default export (export) default XXX modules / / to import the from XXX 'XXX' to introduce the module in the form of "allowSyntheticDefaultImports" : true}Copy the code
/ / configuration before
import * as React from 'react';
import * as ReactDOM from 'react-dom';

/ / configuration
import React from 'react';
import ReactDOM from 'react-dom';
Copy the code

4. Load antD component library on demand

  • Ts-loader is used here to translate TS. See Webpack’s existing Typescript translation for more alternatives

.babelrc

{
  "presets": [
    "@babel/preset-react"."@babel/preset-env"]."plugins": [["import",
      {
        "libraryName": "antd"."libraryDirectory": "es"."style": "css"
        /* `style: true*/}]]}Copy the code

tsconfig.json

{
  "compilerOptions": {
    "target": "es5"."jsx": "preserve"./ / keep JSX. }Copy the code

webpack.config.js

{
  test: /\.tsx? $/.use: [
      'babel-loader'.'ts-loader']},Copy the code

5. Declare the ref type created with React. CreateRef ()

/ / the source code
// interface RefObject<T> {
// readonly current: T | null;
// }

const ref1:React.RefObject<HTMLDivElement> = React.createRef();

const inputRef = React.createRef<Comp>();
class EditScene extends React.Component<Props> {
	inputRef:React.RefObject<Comp> 
    constructor(props) {
        super(props);
      	this.inputRef = React.createRef<Comp>(); }}Copy the code

6. React + redux + React-redux project: Using @connect decorator works fine, but when combined with TS, an error is reported

Segmentfault.com/a/119000001…

import {ComponentClass} from 'react'
import {
    connect as nativeConnect,
    MapDispatchToPropsParam,
    MapStateToPropsParam
} from 'react-redux'
import {withRouter as nativeWithRouter} from 'react-router-dom'

export type ComponentDecorator<P = any> = <T extends ComponentClass<P>>(WrappedComponent: T) = > T

export const connect: <P, S>(
    mapState: MapStateToPropsParam<Partial<P>, P, S>,
 // mapDispatch? : MapDispatchToPropsParam
      
       , P>
      

mapDispatch? :any ) => ComponentDecorator = nativeConnect as any; export const withRouter: ComponentDecorator = nativeWithRouter as any; Copy the code

7. React + redux + react-Redux project: When using the mapStateToProps(state) function, you want to declare a type for state in the repository

  • With the help of a ReturnType
// rootReducer.ts
import {combineReducers} from 'redux';
import {connectRouter} from 'connected-react-router';
import history from '.. /history';
import evidenceEdit from './evidence';
import common from './common';
import work from './work';
import setScene from './set-scene';

let reducers = {
    common,
    work,
    setScene,
    evidenceEdit,
    router: connectRouter(history)
};

// Use ReturnType to infer state shapes from rootReducer
// export type AppState = ReturnType<typeof rootReducer>
export type AppState = {
    [key in keyof typeof reducers]: ReturnType<typeof reducers[key]>
}

const rootReducer = combineReducers(reducers);

export default rootReducer;
Copy the code
/ / setScene module
import * as types from '.. /types/action-types';
import {appEditAction} from '.. /actions/common';

export interface SetSceneState {
    loadSuccess: boolean;
    loadProgress: number;
}

let initState: SetSceneState = {
    loadSuccess: false,
    loadProgress: 0};export default function (state: SetSceneState = initState, action: appEditAction) {
    switch (action.type) {

        case types.SCENE_DATA_LOADSUCCESS: {
            return{... state, loadSuccess: action.payload.success}; }case types.SCENE_DATA_LOADINGPROGRESS: {
            return{... state, loadProgress: action.payload.num}; }default:
            returnstate; }}Copy the code

use

React + redux + react-redux project: Want to declare the type for action Creator

// In the Mesh component
import workActions from "@store/actions/work";

interface MeshProps {
    // I started by saying that each time I had to re-declare the function in the Props of the component
    // updateSceneData? : (workId: string,data) => appEditAction;updateData? :typeof workActions.updateData;
}

@connect(null, {
    updateData: workActions.updateData,
})
class Mesh extendsReact.Component<MeshProps> {... }Copy the code
// store/actions/work.ts

import * as types from '.. /types/action-types';
import {appEditAction} from "@edit-store/actions/common";

export default {
    updateWorkData(workId: string, data: any): appEditAction {
        return {type: types.UPDATE_WORK_ASYNC, payload: {workId, data}}
    }
}
Copy the code

React + redux + react-redux project: give the react component Props declaration type

import * as React from 'react'; import {RouteComponentProps} from 'react-router'; import {connect} from "@store/connect"; import {AppState} from "@store/reducers"; import commonActions from "@store/actions/commonActions"; The component may have four property sources // 1. the return value of mapStateToProps // 2.actions object type // 3. Other attributes passed in from the parent component // // interface Props {// loadProgress? : number; // markVisible? : boolean; // setMarkVisible? : typeof commonActions.setMarkVisible; // } function mapStateToProps(state: AppState) { const {markVisible,loadProgress} = state; return { markVisible, loadProgress, }; Type StateProps = ReturnType<typeof mapStateToProps>; type DispatchProps = typeof commonActions; interface IParams {} type RouteProps = RouteComponentProps<IParams>; type Props = StateProps & RouteProps & DispatchProps & {}; @connect(mapStateToProps, { setMarkVisible: commonActions.setMarkVisible }) export default class App extends React.PureComponent<Props, any> { render() { const {markVisible, loadProgress} = this.props; return (<div > {markVisible} {loadProgress} </div>); }}Copy the code

React + redux + redux-redux project: Want to declare type for redux-thunk

Redux Thunk has a built-in type ThunkAction that we can use like this:

// src/thunks.ts

import { Action } from 'redux'
import { sendMessage } from './store/chat/actions'
import { AppState } from './store'
import { ThunkAction } from 'redux-thunk'

export const thunkSendMessage = (
  message: string
): ThunkAction<void, AppState, null, Action<string> > = >async dispatch => {
  const asyncResp = await exampleAPI()
  dispatch(
    sendMessage({
      message,
      user: asyncResp,
      timestamp: new Date().getTime()
    })
  )
}

function exampleAPI() {
  return Promise.resolve('Async')}Copy the code

Use webpack module.hot to warn that there is no type definition

$NPM install --save @types/webpack-envCopy the code
if(process.env.NODE_ENV ! = ='production') {
    if (module.hot) {
        module.hot.accept('./reducers'.(a)= >store.replaceReducer(rootReducer)); }}Copy the code

12. tsconfig-paths-webpack-pluginThis package maps the path configuration items in tsconfig.json to the Webpack configuration, eliminating the need to configure path mapping in the Alias configuration items in webpack

React function component declaration

interface Greeting { name: string; age: number; } const Hello:React.FC<Greeting> = (props) => <h1>Hello {props.name}</h1>; // It is recommended to use the second const Hello2 = (props:Greeting) => <h1>Hello {props. Name}</h1>;Copy the code

14. How to write HOC for React + TS

import React, { Component } from 'react';

import HelloClass from './HelloClass';

interface Loading {
    loading: boolean
}

// HOC can accept either a class component or a function component, so the parameter is of type react.ponentType
/ / source: type the pop3access.componenttype < P = {} > = ComponentClass < P > | FunctionComponent < P >;
function HelloHOC<P> (WrappedComponent: React.ComponentType<P>) {
    return class extends Component<P & Loading> {
        render() {
            const{ loading, ... props } =this.props;
            returnloading ? <div>Loading... </div> : >; }}}export default HelloHOC(HelloClass);
Copy the code

15. Quickly get the event parameter type of the event handler

class Login extends React.Component <Props>{ handlerLinkBtnClick = (ev) => { console.log(ev); this.props.historyGo('./register'); }; handlerLinkBtnMouseMove = (ev) => { console.log(ev); }; render() { return ( <div> <header> <p >This is Login Page </p> <div className={styles.linkBtn} onMouseMove={this.handlerLinkBtnMouseMove} onClick={this.handlerLinkBtnClick}> Go to Register Page </div> </header> </div> ); }}Copy the code

To get the parameter type of the current event handler, hold Down Ctrl and hover over the event name

Use 16.@babel/preset-typescriptCompile the TS project and use ittsc --watchThe ts file in the test project will receive the following error

The reason is that “@types/react-redux”: “7.1.5” is installed in the project. The new version of react-Redux already provides its own type definition, so @types/ React-redux is no longer required.

Stackoverflow.com/questions/4…

Webpack translates existing Typescript schemas

Juejin. Cn/post / 684490…

Recommended reading

Do you really understand the React lifecycle

React Hooks 【 nearly 1W words 】+ project combat

React SSR: + 2 projects

Cookie, Session, Token, JWT