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
- 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
- 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