1. Use of the new keyword in types

The generic

Use class types in generics

When TypeScript uses generics to create factory functions, you need to reference the class type of the constructor. For instance,

function create<T>(c: {new(): T; }): T {// new() return new c(); }Copy the code

A more advanced example uses stereotype attributes to infer and constrain the relationship of a constructor to a class instance.

class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!
createInstance(Bee).keeper.hasMask;   // typechecks!
Copy the code

What is new() in Typescript? The createInstance argument is A type of class T where the constructor has no arguments. Wrote the test code in doubt:

CreateInstance return new c(); (c: new () => A) c is A class, not an instance of the (c: Animal) class. We know that js has no classes, and ES6 classes are just syntactic candy, which is still a function when compiled. So to modify a class is to modify a function, but you’re modifying a constructor, so there’s a new in front.


interface

The difference between the static and instance parts of a class

We’re using keywords here as wellnew()Error in first example, official explanation:When you work with classes and interfaces, you should know that classes have two types: the type of the static part and the type of the instance. You’ll notice that you get an error when you use the constructor signature to define an interface and try to define a class that implements the interface: This is because when a class implements an interface, only the instance part is type checked. Constructor exists in the static part of the class and is therefore outside the scope of inspection. Therefore, we should directly manipulate the static part of the class. In the following example, we define two interfaces, ClockConstructor for the constructor and ClockInterface for the instance method. To make it easier to define a constructor, createClock, that creates an instance with the type passed in.

interface ClockConstructor { new (hour: number, minute: number): ClockInterface; } interface ClockInterface { tick:()=>void; } function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {// This is the same class type used in generics, return new ctor(hour, minute); // Requires a constructor class with two parameters of type ClockInterface, } class DigitalClock implements ClockInterface {constructor(h: number, m: number) { } tick() { console.log("beep beep"); } } class AnalogClock implements ClockInterface { constructor(h: number, m: number) { } tick() { console.log("tick tock"); } } let digital = createClock(DigitalClock, 12, 17); let analog = createClock(AnalogClock, 7, 32);Copy the code

Because the first argument to createClock is of type ClockConstructor, in createClock(AnalogClock, 7, 32), AnalogClock is checked to see if it matches the constructor signature.

When combined with the react interface writing,

interface Component<P = {}, S = {}> extends ComponentLifecycle<P, S> { } class Component<P, S> {// this is all instance methods and properties constructor(props? : P, context? : any); // Disabling unified-signatures to have separate overloads. It's easier to understand this way. // tslint:disable:unified-signatures setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback? : () => any): void; setState<K extends keyof S>(state: Pick<S, K>, callback? : () => any): void; // tslint:enable:unified-signatures forceUpdate(callBack? : () => any): void; render(): JSX.Element | null | false; // React.Props<T> is now deprecated, which means that the `children` // property is not available on `P` by default, even though you can // always pass children as variadic arguments to `createElement`. // In the future, if we can define its call signature conditionally // on the existence of `children` in `P`, then we should remove this. props: Readonly<{ children? : ReactNode }> & Readonly<P>; state: Readonly<S>; context: any; refs: { [key: string]: ReactInstance }; } interface ComponentClass<P = {}> { new (props? : P, context? : any): Component<P, ComponentState>; // Here we modify Component to specify react constructor propTypes? : ValidationMap<P>; // The following are all static methods and property contextTypes. : ValidationMap<any>; childContextTypes? : ValidationMap<any>; defaultProps? : Partial<P>; displayName? : string; }Copy the code

Interface Component = class Component; interface Component = class Component; interface Component = class Component; Does this mean that the class Component is an implementation of the interface? The index.d.ts file should not be in the.ts file.

interface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> { } class Component<P, S> { static contextType? : Context<any>; // TODO (TypeScript 3.0): unknown context: any; constructor(props: Readonly<P>); /** * @deprecated * @see https://reactjs.org/docs/legacy-context.html */ constructor(props: P, context?: any); // We MUST keep setState() as a unified signature because it allows proper checking of the method return type. // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257 // Also, the ` | S` allows intellisense to not be dumbisense setState<K extends keyof S>( state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null), callback?: () => void ): void; forceUpdate(callback? : () => void): void; render(): ReactNode; readonly props: Readonly<P> & Readonly<{ children? : ReactNode }>; state: Readonly<S>; /** * @deprecated * https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs */ refs: { [key: string]: ReactInstance }; }Copy the code

The interface Component does not declare anything, and the lifecycle methods are inherited. SetState, forceUpdate, and render declarations are all in the class Component. This. SetState in the Component is also located in the class Component. We know that the interface Component is used to modify the instance of the Component, but we cannot describe the static properties of the Component, such as contextType. Interface ComponentClass Interface ComponentClass Interface ComponentClass Interface ComponentClass

interface ComponentClass<P = {}, S = ComponentState> extends StaticLifecycle<P, S> { new (props: P, context? : any): Component<P, S>; propTypes? : WeakValidationMap<P>; contextType? : Context<any>; contextTypes? : ValidationMap<any>; childContextTypes? : ValidationMap<any>; defaultProps? : Partial<P>; displayName? : string; }Copy the code

ComponentClass defines a class Component, and defines static Component members.

Interface ClockConstructor {new (hour: number, minute: number); contextType: any; } interface ClockInterface {tick: () => void; } class DigitalClock implements ClockInterface {constructor(h: number, m: number) {} tick() { console.log("beep beep"); } } function createClock( ctor: ClockConstructor, hour: number, minute: number ): ClockInterface {// This is the same class type used in generics, return new ctor(hour, minute); // We need a constructor class with two parameters of type ClockInterface, only the two are written differently}Copy the code

ContextType = contextType; contextType = contextType; contextType = contextType; contextType = contextType;

class DigitalClock implements ClockInterface { static contextType; Constructor (h: number, m: number) {} tick() {console.log("beep beep"); } } let digital = createClock(DigitalClock, 12, 17);Copy the code

Add static contextType to DigitalClock, ClockInterface is used to describe the instance of the class, ClockConstructor is used to describe the class, It’s ok to describe the static properties of a class using ClockConstructor, but how to describe the static properties of an instance? React creates a.ts file with the same interface and class

interface ClockInterface {}

class ClockInterface {
  tick(): void;
}
Copy the code

If you post the code to the d.ts file, you will not get an error.

interface ClockInterface {
  name: string;
}

class ClockInterface {
  tick(): void;
  static jump(): void;
}

Copy the code

Using this interface,

class DigitalClock implements ClockInterface {
  static jump: () => {};
  name: string = "";
  tick() {
    console.log("beep beep");
  }
}

DigitalClock.jump();

Copy the code

Type DigitalClock. Or static j in vscode, so you can use the same interface and class to describe static properties of a class in d.ts

At this point, the use of the new() keyword in types is pretty clear.

Class decorator
function classDecorator<T extends {new(... args:any[]):{}}>(constructor:T) { return class extends constructor { newProperty = "new property"; hello = "override"; } } @classDecorator class Greeter { property = "property"; hello: string; constructor(m: string) { this.hello = m; } } console.log(new Greeter("world"));Copy the code

2. Use of decorators

What are decorators in JavaScript?

3. Different ways to declare function types

1. The most common way

Function Declaration is a normal named Function

  function add(x: number, y: number): number {
    return x + y
  }
Copy the code
2. Function Expression Type declaration
  • 1. This is an assignment after an anonymous or named function, easier to understand the writing method, all in the function body write type
  handle = (
    baseValue: number,
    increment: number,
  ): number => {
    return baseValue
  }
Copy the code
  • 2. Define types for variables, and also define types inside functions
  handle: (baseValue: number, increment: number) => number = (
    baseValue: number,
    increment: number,
  ): number => {
    return baseValue
  }
Copy the code
  • 3. Extract the type of the variable to the interface
interface IHandle {
  (baseValue: number, increment: number): number
}

 handle: IHandle = (baseValue: number, increment: number): number => {
    return baseValue
  }
Copy the code

Since the previous variable declares the interface, the type in the following function can be removed

interface IHandle {
  (baseValue: number, increment: number): number
}

 handle: IHandle = (baseValue,increment)=> {
    return baseValue
  }
Copy the code

But there was a problem

interface IHandle {
  (baseValue: number, increment: number): number
}

 handle: IHandle = (baseValue)=> {
    return baseValue
  }
Copy the code

Increment increment is not an optional parameter

Check out the functions section of the typescript documentation

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void;
}
Copy the code

The interface written this way is strikingly similar to the interface declaration of function types,

interface IHandle {
  (baseValue: number, increment: number): number
}
Copy the code

The interface declaration of a function type is anonymous, while the function declaration of the above object type is named

Var SRC :UIElement = function() {}  // Type '() => void' is not assignable to type 'UIElement'. // Property 'addClickListener' is missing in type '() => void'. // var src: UIElement interface UIElement { addClickListener(name: string): void } var src: UIElement = {addClickListener() {},} var SRC: UIElement = {name: string, age: number) {}, } // Type '{ addClickListener(name: string, age: number): void; }' is not assignable to type 'UIElement'. // Types of property 'addClickListener' are incompatible. // Type '(name: string, age: number) => void' is not assignable to type '(name: string) => void'.Copy the code

Interfaces are used to modify variables, including parameters of functions, and return values, but not named functions

interface IHandle { props? : object (baseValue: number, increment: number): Function source(baseValue) {return baseValue} function source(baseValue): IHandle {return baseValue}Copy the code

3. How to modify a class (class type)

class Greeter { static standardGreeting = "Hello, there"; greeting: string; greet() { if (this.greeting) { return "Hello, " + this.greeting; } else { return Greeter.standardGreeting; } } } let greeter1: Greeter; greeter1 = new Greeter(); console.log(greeter1.greet()); let greeterMaker: typeof Greeter = Greeter; greeterMaker.standardGreeting = "Hey there!" ; let greeter2: Greeter = new greeterMaker(); console.log(greeter2.greet());Copy the code
  1. We usually modify an instance of a class, but typeof is a good way to modify a class,

In this case, Greeter1 is the same as we saw before. We instantiate the Greeter class and use this object. Same thing we saw before. After that, we use classes directly. We created a variable called greeterMaker. This variable holds the class or holds the class constructor. We then use typeof Greeter, which means take the typeof the class, not the typeof the instance. Or, more accurately, “Tell me the type of the Greeter identifier,” which is the type of the constructor. This type contains all the static members and constructors of the class. Then, as before, we use new on greeterMaker to create an instance of Greeter. That is, use typeof ClassName

interface IPerson { age: number; } class Person { age: 99; } let p: typeof IPerson = Person; // This is an errorCopy the code

  1. Use the constructor interface to decorate the class
interface IPerson {
  age: number;
}

interface IPersonConstructor {
  new (): IPerson;
}

class Person {
  age: 99;
}

let p: IPersonConstructor = Person;
Copy the code

Previous to new (): IPerson; The constructor returns an instance of Person(). New (): IPerson; It makes sense that the constructor returns IPerson

4. keyof

For example, interface a{a1: ‘a1’; a2: ‘a2’; . . a100: ‘a100’; } and then to inherit the interface type of the value of a certain value Such as the type anum = ‘a1’ | ‘a2’ | ‘a3’ |… | ‘a100, should how to write?

type anum = typeof a.a1 | typeof a.a2 | typeof a.a3 | …. | typeof a.a100;

Is there an easy way to write that that interface might change at any time

A1 to A100?

For the whole

And it may have been added up to a100

I want to add only interface type when adding, so I don’t need to modify it. Is there any way

Key or value

value

5. Exclamation mark after function name (non-empty assertion operator)

onOk = () => { this.props.onOk! (this.picker && this.picker.getValue()); this.fireVisibleChange(false); }Copy the code

The react-Component /m-picker group asks for non-null operators

Search the typescript 2.0 documentation and find something called the non-null assertion operator

Function validateEntity(e? : Entity) {// If e is null or an invalid Entity, an exception will be thrown} function processEntity(e? : Entity) { validateEntity(e); let s = e! .name; // Assert e is non-null and access the name attribute}Copy the code

Json with “strictNullChecks”: true, so vscode will automatically detect null and undefined

6. Ts difference set operation

Add support for literal type subtraction

React high-order components use functions TypeScript in React high-order components

7. Method rewrite

Method overriding subclasses must have the same arguments as the parent class, otherwise an error will be reported

8. Remaining parameters

const renderWidget = ({ field, widget, ... restParams }) => { const min = restParams.min; };Copy the code

What does restParams say about the rest of this code

const renderWidget = ( { field, widget, ... restParams }: { field: string; widget: string; restParams: [key: string]: any }, idx: number, primaryField: string ) => { const min = restParams.min; };Copy the code

But the error is still reported, and the restParams should have been left out

const renderWidget = ({ field, widget, ... restParams }: { field: string; widget: string; [key: string]: any; }) => { const min = restParams.min; };Copy the code

Destructuring a function parameter object and… rest

9. Tuple inference

Saw that in the group of question, why is finally deduced string | number

type TTuple = [string, number];

type Res = TTuple[number]; // string|number

Copy the code

If I don’t understand it at first, I’ll rewrite it myself

type TTuple = [string, number];

type Res = TTuple[string]; 
Copy the code

Try writing again

type TTuple = [string, number]; type Res = TTuple[0]; //string type Res1 = TTuple[1]; //number type Res2 = TTuple[2]; / / an errorCopy the code

TTuple[2]An error

TTuple[number] = TTuple[number]; TTuple[number] = TTuple[number]; TTuple[number] = TTuple[number]; TTuple [number] why return string | number because do not specify a specific index, can only infer that two possible, string or number

Think of getting the type of the interface’s property

interface IProps { name: string; age: number; } type IAge = IProps["name"]; // string const per: IAge = "geek";Copy the code

10. Generic interface differences

The following implements code similar to Record

interface Collection<T extends string, U> {
  [P in T]: U;
}

type Collection33<K extends string, T> = {
  [P in K]: T;
};
Copy the code

Type is not a problem, interface is not a problem

11. The function’s this argument

function toHex() {
  return this.toString(16);
}
Copy the code

The this of a function declared by function is determined at call time. To limit the type of the caller, a function in TS can limit the type of the caller with this

function toHex(this: number) {
  return this.toString(16);
}

const x = toHex.call("xxx");
Copy the code

In this case, VScode will give an error message. Simply creating a TS file will not generate an error message. You need to configure tsconfig.json in the root directory of the project.

{"compilerOptions": {"strict": true,// set to strict mode "target": "ES5", "noImplicitThis": true// This setting is invalid}}Copy the code

12. Infer keyword

The Infer keyword can be understood as the declaration keyword of generic variables. Some of the built-in types of INFER are used by TS

  • Gets the function parameter type
/** * Obtain the parameters of a function type in a tuple */ type Parameters<T extends (... args: any) => any> = T extends (... args: infer P) => any ? P : never;Copy the code

example

type Func = (animal: Animal) => void;
type Param = Parameters<Func>;
Copy the code

T extends (… Args: Infer P) => Any is a whole, and determines whether T is a function and then concludes the result according to conditions

  • Gets the return value type of the function
/** * Obtain the return type of a function type */ type ReturnType<T extends (... args: any) => any> = T extends (... args: any) => infer R ? R : any;Copy the code

example

type NextFunc = (animal: Animal) => number;
type CC = ReturnType<NextFunc>;
Copy the code

The important thing to note here is that generics pass in types, not concrete functions

function show(animal: Animal) {
  return 99;
}
type NextFunc = (animal: Animal) => number;
type CC = ReturnType<show>;
Copy the code

Typeof show can be used to infer the typeof the function

function show(animal: Animal) {
  return 99;
}
type NextFunc = (animal: Animal) => number;
type CC = ReturnType<typeof show>;

Copy the code

So you don’t get an error