Declaration, this article is my study and network collation, if involved in infringement, please contact me to delete as soon as possible
Type 1.
1.1 Basic Types
The numerical
let value: number = 10
value = 0o11 // Binary, base 8, etc
Copy the code
Boolean
let value: boolean = true
value = false
Copy the code
string
let value: string = 'hello'
Copy the code
1.2 Array and tuple types
An array type
A:
let arr1: Array<number> // Defines an array that can only hold numbers
arr1 = [1.true.2] // TS 2322 type error
Copy the code
Method 2:
let arr2: string[] // Defines an array that can only hold strings
Copy the code
Union type:
let arr3: (number | string) []// Define an array that can only hold numbers and strings
Copy the code
Any type:
let arr4: any // An array can hold any type of data
Copy the code
A tuple type
The tuple type in TS is an extension of the array type. It is mainly used to create data of fixed length and fixed data type
// Create an array that can hold only 3 data
// The first data must be of type string, the second must be of type number, and the third must be of type Boolean
let arr: [string.number.boolean];
Copy the code
1.3 Enumeration Types
Use enumerated
The enum type is a complement to the enum keyword of the array type
Enumerations are generally used for several fixed values, such as only four seasons in a year and only male and female
enum Gender {
Male,
Female
}
Copy the code
Use enumerated
let value: Gender; // Defines a variable that can only hold Male/Female
value = Gender.Male
value = Gender.Female
Copy the code
Matters needing attention
TS enumerations are by nature numeric types, so assigning a value to a numeric type is not an error
Gender.Male / / 0
Gender.Female / / 1
Copy the code
The default value of an enumeration type is from top to bottom, ascending from 0, but we can specify the value of the enumeration manually
If the previous enumeration value is specified manually, the subsequent ones are incremented
enum Gender {
Male = 5,
Female
}
Gender.Female / / 6
Copy the code
If the latter enumeration value is specified, the preceding values are incremented from 0
enum Gender {
Male,
Female = 6
}
Gender.Male / / 0
Copy the code
Enumeration values can be changed at will
enum Gender {
Male = 100,
Female = 12
}
Copy the code
Not only can we get values by enumerating keys, we can also get keys by values
enum Gender {
Male = 6
}
Gender[6] // Male
Copy the code
How does this work?
// Compiled JS
'use strict'
var Gender
(function (Gender) {
Gender[(Gender['Male'] = 6)] = 'Male'
})(Gender || (Gender = {}))
// 👇 is what it looks like 👇
let obj = {}
obj[obj["male"] = 3] = "male"
obj = { 3: "male".male: 3 }
Copy the code
1.4 Any and void types
Any type
Any type, so if we don’t know what type this value is, we can give it any, okay
Generally used to define a variable with strong generality, or saved from other places do not know what type of variable
let val: any;
val = 0
val = "aa"
val = true
Copy the code
Void type
Void is the opposite of any, indicating that there is no type, and is generally used to return values from functions
TS only null/undefined can be void
function test() :void {
console.log('test void')}let val: void; // define a variable that can only be stored as null or undefined
Copy the code
1.5 The Never and Object types
Never type
Represents types that never exist, typically for functions that throw exceptions or return no value at all
function demo() :never {
throw new Error('test never')}function demo2() :never {
while(true) {}}Copy the code
The object type
Represents an object type that can store only object type data
let obj: object
obj = {
a: 1
}
Copy the code
1.6 Type Assertion
What is a type assertion
Type assertions in TS are really more like type conversions, where you can cast one type to another.
Type assertions are like saying to the compiler: I know what I’m doing, don’t you need to check
Using type assertions
The first way:
let str: any = "abc" // At this point, STR is of type any, so we can't use string functions
// str.length // an error is reported
let len = (<string>str).length // Convert any to string
Copy the code
The second way:
let len2 = (str as string).length // Convert to string
Copy the code
Pay attention to
In enterprise development, it is more recommended to use the AS approach for type assertion because of compatibility issues in the first TSX file
1.7 Automatic type inference
1. What is automatic type inference
You don’t have to tell the compiler what type it is, the compiler knows what type it is, right
2. Automatically infer information based on the initial value
// If it is defined first and then initialized, there is no automatic inference
let value;
value = 123;
// If it is initialized at the same time as defined, TS will automatically do type inference
let value = 123 // -> let value: number = 123
value = 'aaa' // error
// The union type can also be inferred automatically
let arr = [1.'a'] // -> let arr: (number | string) = [1, 'a']
arr = ['a'.2.false] // error
Copy the code
3. Automatic inference based on context type
window.onmousedown = (event) = > { // Automatically deduce that event is of MouseEvent type
event.target
}
Copy the code
2. The interface
2.1 Using Interfaces
Like the basic types number, Boolean, String, and enum, an interface is a type
Define an interface type
interface FullName {
firstName: string
lastName: string
}
Copy the code
If an interface is used to constrain a variable or parameter, the interface must be followed, and the type, number, and position must be correct.
function getFullName({fistName, lastName}: FullName) {
return `${firstName} - ${lastName}`
}
getFullName({'aa'.'bb'}) // right
getFullName({true.'bb'}) // wrong
getFullName({'aaa'}) // wrong
getFullName({'a'.'b'.'c'}) // wrong
Copy the code
What if we want one more, one less, one less?
2.2 Optional Variables
If less than one or more than one, then you need to join in the definition, right? To judge that this can be omitted
interface FullName {
firstName: stringlastName? :stringmiddleName ? :string
}
// The top interface, 'lastName' and 'middleName' are both optional
Copy the code
2.3 Index Signature
If you have one or more parameters, you need to bypass the check
Method 1: Use a type assertion:
getFullName({
firstName: 'aa'.lastName: 'bb'.notName: 'nihao'
} as FullName)
Copy the code
Type assertions allow this code to bypass type checking directly
Method 2: Use variables [not recommended]
let person = {
firstName: 'aa'.lastName: 'bb'.notName: 'nihao'
}
getFullName(person)
Copy the code
Method 3: Use index signatures
interface FullName {
firstName: string
lastName: string
[propName: string] :any
}
Copy the code
When defining the interface, the propName above means that the key can only be string and the value can be of any type (any).
getFullName({firstName: 'aa'.lastName: 'bb'.notName1: 'aaaaa'.notName2: 'bbb'})
Copy the code
The index sign
Index signatures are used to describe types that are “indexed”, such as ARR [10] or obj[“key”]
// Defines an interface where all variables/parameters bound in the future must have keys and values of type string
interface FullName {
[propName: string] :string
}
let obj: FullName {
// As long as the key value meets the conditions of the index signature, no matter how many
aa: 'aaa'.bb: 'bbb'.// cc: false, // error
false: '666' // No error is reported, because no matter what type the key is, it will be converted to string
}
Copy the code
2.4 Read-only Properties
Make an object’s properties change their values only when the object is created
// The lastName of this interface could not be reassigned
interface FullName {
firstName: string
readonly lastName: string
}
let myName: FullName {
firstName: 'alex'.lastName: 'white'
}
myName.firstName = 'tom' // right
myName.lastName = 'zhang' // wrong
Copy the code
TS extends the read-only property internally to a read-only array
let arr: Array<string> = ['a'.'b'.'c']
arr[0] = '123' // Arrays are readable and writable by default
// Define a read-only array
let arr2: ReadonlyArray<string> = ['a'.'b'.'c']
arr2[0] = '123' / / an error
Copy the code
2.5 Functional Interfaces
In addition to qualifying objects through interfaces, we can also qualify functions through interfaces
// The first argument must be of type number and the second argument must be of type number
// Return must be of type number
interface SumInterface {
(a: number.b: number) :number
}
let sum: SumInterface = (num1: number.num2:number) :number= > {
return num1 + num2
}
let res = sum(1.2)
Copy the code
2.6 Mixed Interfaces
Interfaces have both object attributes and functions, and those types need to be constrained by their conditions
interface CountInterface {
():void
count: number
}
let getCounter = (function() :CountInterface {
// The interface requires data to be a function with no arguments and no return values
// An object with a count attribute
// when fn is used as a function, it conforms to the qualification of function interface
// When fn is used as an object, it conforms to the object attributes of the interface
let fn = <CountInterface>function() {
fn.count++
console.log(fn.count)
}
fn.count = 0
return fn
})()
Copy the code
2.7 Interface Inheritance
Interfaces in TS and classes in JS can be inherited
interface LengthInterface {
length: number
}
interface WidthInterface {
width: number
}
// Redundant code can be inherited directly
interface RectInterface extends LengthInterface, WidthInterface {
// length: number
// width: number
color: string
}
Copy the code
2.8 Interface Merging Symptom
When we define many interfaces with the same name, the contents of the interfaces will be merged.
interface TestInterface {
name: string
}
interface TestInterface {
age: number
}
// Automatically merge to
interface TestInterface {
name: string
age: string
}
Copy the code
3. The function
3.1 Using Functions
The functions of TS are mostly the same as the functions of JS
// Name the function
function fn1(a: string) :void {
console.log(a)
}
// Anonymous function
let fn2 = function(b: string) :void {
console.log(b)
}
// Arrow function
let fn3 = (c: string) :void= > {
console.log(c)
}
Copy the code
3.2 Complete format of the function
The complete format of a function in TS should consist of two parts: the definition and implementation of the function
// Define a function
let addFn: (a: number, b: number) = > number;
// Implement the function as defined
addFn = (x: number.y: number) :number {
return x + y
}
Copy the code
Write it in one step
let addFn(a: number.b: number) = >number = function (x: number, y: number) :number {
return x + y
}
Copy the code
Further optimize the writing method
// Automatically derive the corresponding data type according to the function definition
let addFn(a: number.b: number) = >number = function (x, y) {
return x + y
}
Copy the code
3.3 Function Declaration
As we can see from the above, we can declare a function first and implement it later. Use type to declare functions
// Declare a function
type addFn(a: number.b: number) = >number
// Implement a function based on the declaration
let add: addFn = (x: number.y: number) :number= > {
return x + y
}
Copy the code
shorthand
// Declare a function
type addFn(a: number.b: number) = >number
// Implement a function based on the declaration to automatically derive the type
let add: addFn = (x, y) = > {
return x + y
}
Copy the code
3.4 Function Overloading
Function overloading is when a function of the same name can perform different functions based on different parameters
// Define function overloading
function getArray(x: number) :number[]
function getArray(str: string) :string[] // Implement function overloadfunction getArray(value: any) :any[] {
if(typeof value === 'number') {
let arr = []
for(let i = 0; i <= x; i++) {
arr.push(i)
}
return arr
} else if (typeof value === 'string') {
return value.split(' ')}else {
console.log('argument must be a string or number')
return[]}}Copy the code
3.5 Parameters of a Function
Optional parameters
Add? To parameter definition Indicates that the parameter can be omitted
// Add two or three numbers
function add(x: number, y:number, z? :number) :number {
return x + y + (z ? z : 0)
}
add(2.3)
add(2.3.4)
Copy the code
You can also use this optional parameter when a function is overloaded to make it more robust
function add(x: number, y:number) :number
function add(x: number, y:number, z:number) :number
function add(x: number, y: number, z? :number) :number {
return x + y + (z ? z : 0)}Copy the code
Note:
- The optional parameters can be one or more
- The preceding parameters are optional, and the following parameters must be optional as well
The default parameters
See ES6- Function parameter defaults
function add(x: number, y: number = 10) :number {
return x + y
}
add(10) / / 20
Copy the code
The remaining parameters
See ES6- Function extension operators
function add2(x: number. args:number[]) :number {
let sum: number = 0
let each: number
for (each of args) {
sum += each
}
return x + sum
}
console.log(add2(1.3.4.5.6))
Copy the code
4. The generic
4.1 What are Generics
When writing code, we should consider both the robustness of the code and the flexibility and reusability of the code.
Static detection through TS can make our code more robust and secure, but in the robust at the same time lost flexibility and reusability, to solve this problem, TS introduced the concept of generics.
// Requirements: define a method to create an array of specified length, and can fill the array with any content
let getArray = (value: any.length: number = 5) :any[] = > {return new Array(length).fill(value)
}
let arr = getArray(6) // [6, 6, 6, 6, 6]
Copy the code
But using any code directly is no longer robust, and there are some problems:
// 1. Write code without prompting, because the any type does not know whether the length attribute exists
// 2. Even if the code is written wrong, no error will be reported
let res = arr.map(item= > item.length)
Copy the code
Such problems can be avoided by using generics
4.2 Using generics
let getArray = <T>(value: T, length: number = 5): T[] => {
return new Array(length).fill(value)
}
Copy the code
Tell T what type T is when calling a method, and the value and return value will be exactly that type
let arr = getArray<string> ('value')
By default, ts automatically determines the type based on the value passed
let arr2 = getArray(4) // === getArray<number>(4)
Copy the code
4.3 Generic constraints
1. What are generic constraints
By default we can specify a generic type as any type, but in some cases we need to specify a type that meets certain conditions. This is where generic constraints can be used
// Requirements: Any type of generic can be specified, but the specified type must have a length attribute
interface LengthInterface {
length: number
}
let getArray = <T extends LengthInterface>(value: T, length: number = 5): T[] => {
return new Array(length).fill(value)
}
Copy the code
2. Use type parameters in generic constraints
A generic constrained by another generic is called using type parameters in a generic type
// Requirement: define a function to get the value of an object based on the specified key
let getProps = (obj: object.key: string) :any= > {
return obj[key]
}
let obj = {
a: 'a'
}
// undefined, but the code is not robust enough
let res = getProps(obj, 'c')
Copy the code
// Use keyof as the index type
let getProps =
(obj: T, key: K): any => { return obj[key] }
,>Copy the code
Class 5.
5.1 using class
Classes in TS are basically the same as classes in JS
class Person {
name: string Instance attributes are defined before they can be used
age: number
// constructor
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// Instance method
say(): void {
console.log(`my name is The ${this.name}, my age is The ${this.age}`)}// Static attributes
static food: string;
// Static method
static eat(): void {
console.log('I'm eatingThe ${this.food}`)}}let p = new Person('alex'.18)
p.say()
Person.food = 'eggs'
Person.eat()
/ / inheritance
class Student extends Person {
book: string;
constructor(name: string, age: number, book: string) {
super(name, age)
this.book = book
}
say(): void {
console.log('I am the new say')}}let stu = new Student('zs'.19.'Chinese')
stu.say()
Student.food = 'milk'
Student.eat()
Copy the code
5.2 Class attribute modifiers
public
: read onlyprotected
: the protectedprivate
Private:readonly
: read only
5.3 Class method modifiers
public
If you use public to decorate a method, it means that the method is public and can be used inside a class, within a subclass, or outside of itprotected
: A protected method indicates that the method is protected and can be used either internally or within a subclassprivate
If you use a private modifier, that means the method is private and can be used inside the class
// Requirement: There is a base class from which all subclasses inherit, but we do not allow others to create objects from the base class
If you add the modifier protected before the constructor, you cannot create instances based on this class
class Person {
name: string;
protected constructor(name: string) {
this.name = name
}
say(): void {
console.log(this.name)
}
}
class Student extends Person {
age: number;
constructor(name: string, age: number) {
super(name)
this.age = age
}
}
// new Person() returns an error
new Student()
Copy the code
5.4 Optional Class Properties
Attributes that are passable or not, like interface optional attributes
// If an instance attribute is defined in TS, it must be used in the constructor otherwise an error will be reported
class Person {
name: string; age? :number;
constructor(name: string, age? :number) {
this.name = name
this.age = age
}
}
let p = new Person('alex') // Can pass a parameter without error
Copy the code
5.5 Class Parameter Properties
Parameter attributes are used to simplify code
class Person {
constructor(public name: string.public age: number){}}// The class does not write attributes, but the public modifier allows the class to still have attributes
let p = new Person('alex'.13)
console.log(p) // { name: 'alex', age: 13 }
Copy the code
5.6 Class accessors
Intercepts access to object members through getters/setters
class Person {
private _age: number = 0;
set age(val: number) {
if(val < 0) {
throw new Error('Age not less than 0');
}
this._age = val;
}
get age() :number {
return this._age
}
}
let p = new Person()
// p.age = -1 // error !
p.age = 10
console.log(p.age) / / 10
Copy the code
5.7 the abstract class
1. What is abstract class
Abstract classes are used specifically to define classes that you don’t want to create directly from the outside world. Abstract classes are used to define base classes, and abstract classes, like interfaces, are used to constrain subclasses.
// Any class that inherits this abstract class must have a name attribute and a say method
abstract class Person {
abstract name: string;
abstract say(): void;
eat(): void {
console.log(`The ${this.name} is eating`)}}// let p = new Person() // error !
class Student extends Person {
name: string;
constructor(name: string) {
this.name = name
}
say(): void {
console.log(`my name is The ${this.name}`)}}Copy the code
2. Distinction between abstract classes and interfaces
Only constraints can be defined in an interface, not implementations.
Both constraints and concrete implementations can be defined in an abstract class
5.8 Classes and Interfaces
1. Class implementation interface
interface PersonInterface {
name: string;
say(): void;
}
// As long as the class implements the interface, the class must implement the attributes and methods of the interface
class Person implements PersonInterface {
name: string = 'alex'
say(): void{}}Copy the code
2. Interface inheritance classes
class Person {
protected name: string;
age: number;
constructor(name: string, age: number) {
this.name = name
this.age = age
}
say(): void {
console.log(`The ${this.name}-The ${this.age}`)}}// Note: If an interface inherits a class, the interface inherits all attributes and method constraints of that class
// Note: If the inherited class contains protected properties or methods, then only subclasses of that class can implement this interface
interface PersonInterface extends Person {
gender: string;
}
class Student extends Person implements PersonInterface {
gender: string = 'male';
name: string = 'alex';
age: number = 18
say(): void{}}let s = new Student()
s.say()
Copy the code
5.9 Classes and generics
1. A generic class
class Cache<T> {
arr: T[] = [];
add(value: T): T {
this.arr.push(value)
return value
}
all(): T[] {
return this.arr
}
}
let c = new Cache<number>()
c.add(1)
c.add(2)
// c. dd('3') // error, only generic number can be used
console.log(c.all())
Copy the code
6. The enumeration
TS supports numeric enumeration and string enumeration. By default, numeric enumeration is used
6.1 Enumeration of Digits
enum Gender {
Male,
Female
}
console.log(Gender.male) / / 0
console.log(Gender.female) / / 1
Copy the code
Pay attention to the point
- Numeric enumeration values start at 0 and increase by default
- Numeric enumerations can be literal, constant, or computed
- If the previous enumeration values are assigned using constants or computed results, the subsequent enumeration values also need to be manually assigned
6.2 Enumeration reverse mapping
The original value can be obtained from the enumerated value, or the enumerated value can be obtained from the enumerated value
enum Gender {
Male,
Female
}
console.log(Gender.Male) // Get the original value by enumerating values
console.log(Gender[0]) // Get enumerated values from raw values
Copy the code
6.3 Enumeration of Strings
enum Gender {
Male = 'male',
Female = 'woman' // If the previous enumeration was assigned using a string, the subsequent enumeration also needs to be manually assigned
}
console.log(Gender.Male) / / male
Copy the code
Pay attention to the point
-
If the previous enumeration is assigned using a string, the subsequent ones need to be manually assigned as well
-
Unlike numeric enumerations, string enumerations cannot assign values to enumerations using constants or computed results
-
String enumerations cannot get enumeration values from the original values
-
Although string enumerations cannot be assigned using constants or computed results, they can be assigned using values from other internal enumerations
enum Gender { Male = 'male', Female = 'woman', Test = Male } Copy the code
6.4 Heterogeneous Enumeration
Enumerations that contain both numbers and strings are called heterogeneous enumerations
enum Gender {
Male = 6,
Female = 'test'
}
Copy the code
6.5 Enumerator types
We can use enumerators as types
enum Gender {
Male,
Female
}
interface TestInterface {
age: Gender.Male
}
class Person implements TestInterface {
/ / age: Gender. The Male)
// Age: Gender.Female // An error is reported due to type mismatch
age: 0 // Note: Since numeric enumerations are themselves numeric values, there is no error when writing numbers here
}
Copy the code
enum Gender {
Male: 'male'.female: 'woman'
}
interface TestInterface {
age: Gender.Male
}
class Person implements TestInterface {
// age: '男' // error
// age: string // error
// If it is a string enumeration, it must be the value of the enumeration member, not other values
}
Copy the code
6.6 Joint Enumeration Types
We can use an enumerated type as a union type
1. What are federated types
The joint type is connect multiple data through |
/ / (number | string) joint type
let value: (number | string);
value = 0
value = '1'
Copy the code
2. What are joint enumerated types
enum Gender {
Male,
Female
}
interface TestInterface {
age: Gender // age: (Gender.Male | Gender.Female)
}
Copy the code
6.7 Runtime Enumeration
Enumeration is a real stored object after compilation, so it can be used at run time
Code such as interfaces, which are used only for constraints and static checks, will no longer exist when compiled
6.8 Enumeration of Constants
// Common enumeration
enum Gender {
Male,
Female
}
// Constant enumeration
const enum Gender2 {
Male,
Female
}
Copy the code
The difference between:
- Ordinary enumerations generate real objects
- Constant enumerations do not generate real objects
- Instead, the enumeration member’s value is substituted directly to where it is used
- Constant enumerations can reduce page size
7. Compatible
7.1 Type Compatibility
interface TestInterface {
name: string;
}
let p1 = {name: 'alex'}
let p2 = {age: 18}
let p3 = {name: 'alex'.age: 18}
let t: TestInterface;
// Object types and interface types are compatible
t = p1;
t = p2; / / an error
t = p3; // Can also be compatible, can not be less
Copy the code
7.2 Function Compatibility
1. Number of parameters
let fn1 = (x: number, y: number) = > {}
let fn2 = (x: number) = > {}
fn1 = fn2
fn2 = fn1 // The number of errors can be as small as possible
Copy the code
2. Parameter type
let fn1 = (x: number) = > {}
let fn2 = (x: number) = > {}
let fn3 = (x: string) = > {}
fn1 = fn2
fn2 = fn1
fn3 = fn2 // Error, parameter type must be the same
Copy the code
3. Return value type
let fn1 = (): number= > 134
let fn2 = (): number= > 456
let fn3 = (): string= > 'aaa'
fn1 = fn2
fn2 = fn1
fn3 = fn1 // An error is reported. The return value type must be the same
Copy the code
4. Bidirectional covariant of functions
Bidirectional covariance of parameters
let fn1 = (x: (number | string)) = > {}
let fn2 = (x: number) = > {}
fn1 = fn2
fn2 = fn1
Copy the code
Bidirectional covariant of the return value
let fn1 = (x: boolean) :(number | string) = > x ? 123 : 'abc'
let fn2 = (x: boolean) :number= > 456
fn2 = fn1 // Error: cannot assign a value of a union type to a concrete type
fn1 = fn2 // But you can go from concrete type to union type
Copy the code
5. Function overload
function add(x: number, y: number) :number;
function add(x: string, y: string) :string;
function add(x, y) {
return x + y
}
function sub(x: number, y: number) :number;
function sub(x, y) {
return x - y
}
let fn1 = add;
fn1 = sub // Cannot assign less overloaded to more overloaded
let fn2 = sub;
fn2 = add // But it is possible to assign the more overloaded to the less overloaded
Copy the code
7.3 Enumerating Compatibility
1. Numeric enumeration is compatible with numeric values
enum Gender {
Male,
Female
}
let value: Gender;
value = Gender.Male
value = 0
Copy the code
2. Numeric enumerations are incompatible with numeric enumerations
enum Gender {
Male,
Female
}
enum Animal {
Dog,
Cat
}
let value1: Gender = Gender.Male;
value = Animal.Dog; / / an error
Copy the code
3. String enumerations are incompatible with strings
enum Gender {
Male = 'male',
Female = 'woman'
}
let value: Gender = Gender.Male;
value = 'male' / / an error
Copy the code
7.4 Class Compatibility
1. Only instance members are compared, not class constructors and static members
class Person {
public name: string;
// More, not less
// public age: number;
// Static members are not compared
public static age: number;
// Constructors are not compared
constructor(name: string, age: number){}}class Animal {
public name: string;
constructor(name: string){}}Copy the code
2. Private and protected properties of a class affect compatibility
class Person {
// Private and protected properties affect compatibility
private name: string;
protected age: number;
}
class Animal {
private name: string;
}
Copy the code
7.5 Generic compatibility
Generics affect only what is used, not what is declared
interface TestInterface<T> {}
let t1: TestInterface<number>;
let t2: TestInterface<string>;
t1 = t2;
t2 = t1; // Does not affect the declaration section
interface TestInterface<T> {
age: T;
}
let t1: TestInterface<number>; // age: number
let t2: TestInterface<string>; // age: string
t1 = t2; / / an error,
t2 = t1; // Only the part used is affected
Copy the code
8 Advanced Types
8.1 Cross Types
- Format:
type1 & type2 & ...
- Cross typing is merging multiple types into one type
let mergeFn =
(arg1: T, arg2: U): (T & U) => { let res = {} as (T & U); res = Object.assign(arg1, arg2) return res } let res = mergeFn({name: 'aaa'}, {age: 18}) console.log(res) // { name: 'aaa', age: 18 }
,>Copy the code
8.2 Association Type
- Format:
type1 | type2 | ...
- A union type is any type of multiple types
let value: (number | string | boolean);
value = 123
value = 'abc'
value = true
Copy the code
8.3 Type Protection
For a variable of a combined type, the compiler should be told exactly which type it is, either through type assertion or type protection.
let getRandomValue = (): (string | number) = > {
let value = Math.random()
return (value >= 0.5)?'abc' : 123.123
}
If it is a string, the length of the string is obtained; if it is a number, the decimal is omitted
// With type assertion, you need to assert the type every time, so it's a bit of a hassle
if((value as string).length) {
console.log((value as string).length)
}else {
console.log((value as number).toFixed())
}
// So we can also use type protection
// defines a type protection function whose return type is a Boolean type
// The return type of this function is whether the passed argument is of type string
function isString(value: (string | number)) : value is string {
return typeof value === 'string'
}
if(isString(value)) {
console.log(value.length)
}else {
console.log(value.toFixed())
}
Copy the code
// Except that you can tell the compiler that a variable using a combined type is a type by defining a type-protection function
Typeof can also be used for type protection
if(typeof value === 'string') {
console.log(value.length)
}else {
console.log(value.toFixed())
}
Copy the code
Note:
- If you are using
typeof
To implement type protection, then can only use= = =
or! = =
- If you are using
typeof
To implement type protection, then can only protectnumber/string/boolean/symbol
type
// In addition to typeof classes, you can also implement type protection through instanceof
class Person {
name: string = 'alex'
}
class Animal {
age: number = 18
}
let getRandomObject = (): (Person | Animal) = > {
let value = Math.random()
return (value >= 0.5)?new Person() : new Animal
}
let res = getRandomObject()
if(res instanceof Person){
console.log(res.name)
}else {
console.log(res.age)
}
Copy the code
8.4 Special Types
1. Null value detection
There are two special types of TS null/undefined, each with a value of null/undefined
- By default, we can set
null/undefined
Assign to any type - These two types can also be converted to each other by default
let value1: null;
let value2: undefined;
value1 = value2 // ok
value2 = value1 // ok
let value3: number;
value3 = value1 // ok
value3 = value2 // ok
Copy the code
In enterprise development, if you do not want null/undefined to be assigned to arbitrary types, or do not want the two to be converted to each other, you can enable strictNullChecks in tsconfig.json, the previous operation will return an error.
{
"strictNullChecks": true
}
Copy the code
2. Null value detection down conversion
If we want to assign null/undefined to any type after strictNullChecks is enabled, then we need to use the incoming fit type
let value: (number | null | undefined);
Copy the code
3. Optional parameters and optional properties under null check
For optional attributes and optional parameters, if strictNullChecks is turned on, the default data type is union.
Is the current type + undefined
class Person { name? :string / / the default name is of type (string | undefined)
}
function say(name? :number) {
/ / the name is (number | undefined)
}
Copy the code
4. Remove null and undefined detection
Usable! To remove null or undefined detection, meaning that the value must not be null or undefined
function getLength(value: (string | null | undefined)) {
value = 'abc'
return () = > {
// return value.length // error because null and undefined have no length attribute
// return (value || '').length; // Native JS
// return (value as string).length // use type assertion
returnvalue! .length// We know that value must be of type string, so we can use the shorthand for type assertion.}}Copy the code
8.5 Type Aliases
1. What is a type alias
A type alias simply gives a type a new name, but they all represent the same type.
// Give string a type alias called myString
// Both myString and string are represented as strings
type myString = string;
let value: myString;
value = '134'
value = 134 / / an error
Copy the code
Type aliases are used for generics
type MyType<T> = { x: T, y:T }
let value: MyType<number>;
// Future assignments to value must be {x, y} and must all be of type number
value = {
x: 1.y: 1
}
value = {
x: 2.y: '1'
}/ / an error
Copy the code
3. Use yourself in the properties of the type alias
// This is used to define nested, tree-like structures
type MyType = {
name: string; children? : MyType// This needs to be optional, otherwise the loop will be nested and will not stop
}
let value: MyType = {
name: 'aa'.children: {
name: 'bb'.children: {
name: 'cc'
/ /...}}}Copy the code
4. Interface and type aliases are compatible
type MyType = {
name: string
}
interface MyInterface {
name: string
}
let value1: MyType = {name: 'alex'}
let value2: MyInterface = {name: 'tom'}
value1 = value2
value2 = value1 // If the two constraints are of the same type, they can be converted to each other, even if one is an interface and the other is a type alias
Copy the code
5. Similarities and differences between type aliases and interfaces
1. Both can describe properties or methods
type MyType = {
name: string;
say():void;
}
interface MyInterface {
name: string;
say(): void;
}
Copy the code
2. Both allow expansion
Extension interface
interface MyInterface {
name: string;
say: void;
}
interface MyInterface2 extends MyInterface {
age: number
}
let value: MyInterface2 = {
name: 'aaa'.age: 18,
say(): void {
console.log('aaa')}}Copy the code
Extension type alias
type MyType = {
name: string;
say():void;
}
type MyType2 = MyType & {
age: number;
}
let value: MyType2 = {
name: 'aaa'.age: 18,
say(): void {
console.log('aaa')}}Copy the code
3. Type Can declare basic class names, aliases, union types, and tuples, but interfaces cannot
type MyType1 = boolean;
type MyType2 = string | number;
type MyType3 = [string.boolean.number]
Copy the code
4. Interfaces are automatically merged instead of types
interface MyInterface {
age: number
}
interface MyInterface {
name: string
}
// Two interfaces with the same name will be merged
Copy the code
8.6 Literal types
1. What is a literal
A literal is a fixed value in the source code.
For example, numeric literals: 1, 2, 3
String literals: ‘a’, ‘ABC’
2. Literal types in TS
In TS we can use literals as concrete types. When we use literals as concrete types, the value of the type must be the value of the literal.
type MyNum = 1;
let value:MyNum = 1;
value = 2 / / an error
Copy the code
8.7 Identifiable associations
1. What is identifiable association
They have common identifiable characteristics. A type alias that contains types and associations that have common identifiable characteristics
interface Square {
kind: 'square';
size: number
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number
}
interface Circle {
kind: 'circle';
radius: number
}
Copy the code
The above three interfaces all have one thing in common, that is, they all have one attribute, KIND, which is the common identifiable feature
// Shape is a recognizable union because its value is a union because each value of the union has a common recognizable characteristic
type Shape = (Square | Rectangle | Circle);
function area(s: Shape) {
swicth(s.kind){
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.Pi * s.radius ** 2; // ** is the power operator of ES 7}}Copy the code
2. Identifiable joint integrity checks
function area(s: Shape) {
swicth(s.kind){
case "square": return s.size * s.size;
// case "rectangle": return s.width * s.height;
case "circle": return Math.Pi * s.radius ** 2; // ** is the power operator of ES 7}}Copy the code
In enterprise development, if we want to check the integrity of identifiable associations, there are two ways:
A:
- Add a return value to a function
- Open the
strictNullChecks
Method 2:
- Add the default
- Add never
A:
// Add the return value number
function area(s: Shape) :number {
swicth(s.kind){
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.Pi * s.radius ** 2; // ** is the power operator of ES 7}}Copy the code
Turn on strictNullChecks in tsconfig.json
Method 2:
function MyNever(x: never) :never {
throw new Error('Identifiable combined treatment incomplete' + x)
}
function area(s: Shape) {
swicth(s.kind){
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.Pi * s.radius ** 2; // ** is the power operator of ES 7
default: return MyNever(s)
}
}
Copy the code
9. TS senior
9.1 Index Accessors
Using the [] index accessor, we can get the type of an index.
class Person {
name: string;
age: number;
}
// MyType is a string
type MyType = Person["name"]
Copy the code
1. Application scenarios
Requirement: get the specified object, part of the attribute value, put in an array to return
let obj = {
name: 'alex'.age: 18.gender: true
}
function getValues<T.K extends keyof T> (obj: T; keys: K[]) :T[K] []{
let arr = [] as T[K][]
keys.forEach(key= > {
arr.push(obj[key]);
})
return arr
}
let res = getValues(obj, keys['name, age'])
console.log(res) // [alex, 18]
Copy the code
2. Pay attention to the point
Cannot return null/undefined/never
9.2 Mapping Types
1. What is a mapping type
New types are created from old types, which we call mapping types
interface TestInterface {
name: string;
age: number
}
type ReadOnlyTestInterface<T> = {
// Add all keys of the specified type to the current object
readonly [P in keyof T]: T[P]
}
type MyType = ReadOnlyTestInterface<TestInterface>
Copy the code
Of course, you can not only add a constraint, but also remove it
interface TestInterface {
readonly name: string;
readonly age: number
}
type ReadOnlyTestInterface<T> = {
// the minus sign indicates that the constraint is removed, and the plus sign indicates that the constraint is added
-readonly [P in keyof T]: T[P]
}
type MyType = ReadOnlyTestInterface<TestInterface>
Copy the code
Because the operation of generating readable and optional properties is common, TS is encapsulated internally
ReadOnly/Partial
interface TestInterface {
name: string;
age: number
}
// All become readable
type MyType = ReadOnly<TestInterface>
// All are now optional
type MyType2 = Partial<TestInterface>
// It becomes optional and readable
type MyType3 = Partial<ReadOnly<TestInterface>>
Copy the code
2. Pick mapping type
Map parts of the original type to the new type
interface TestInterface {
name: string;
age: number
}
// Passing in name only maps to name
type MyType = Pick<TestInterface, 'name'>
Copy the code
3. Record Mapping type
Map all attributes of one type to another type and create a new type
type Animal = 'person' | 'dog' | 'cat'
interface TestInterface {
name: string;
age: number
}
// will map a new type, about 👇
/* { person: { name: string, age: number, } dog: { name: string, age: number } cat: { name: string, age: number } } */
type MyType = Record<Animal, TestInterface>
let res:MyType = {
person: {
name: 'xx'.age: 18
},
dog: {
name: 'aa'.age: 3
},
cat: {
name: 'bb'.age: 1}}Copy the code
4. Infer based on the mapping type
For Readonly, Partial, and Pick mapping types, you can unpack the mapped type. Restoring the type before the mapping is called unpacking
interface TestInterface {
name: string;
age: number;
}
type TestType<T> = {
+readonly [P in keyof T]: T[P]
}
// Test is a read-only mapping type
type test = TestType<TestInterface>
// So how do we go back?
type UnType<T> = {
-readonly [P in keyof T]: T[P]
}
// Test2 is unpacked
type test2 = UnType<test>
Copy the code
9.3 Condition Types
Determines whether a previous type is a later type or inherits from a later type
If yes, return the first result, if not return the second result
T extends U ? X : Y
Copy the code
type MyType<T> = T extends string ? string : any
// In this case, res is a string
type res = MyType<string>
// Res2 is Boolean
type res2 = MyType<boolean>
Copy the code
1. Infer keyword
Condition types provide a Infer keyword that allows us to define new types within a condition type.
Requirement: Define a type that returns the element type of an array if passed in, or a plain type if passed in.
type MyType<T> = T extends any[]? T[number] : T;
type res = MyType<string[] >// -> string
type re2 = MyType<number> // -> number
Copy the code
Using infer
Infer U is the type of each array element
type MyType<T> = T extends Array<infer U> ? U : T
Copy the code
9.4 Distributed Condition Types
When the detected type is a union type, the condition type is called a distributed condition type
type MyType<T> = T extends any ? T : never
// The detected type is a federated type, that is, a distributed federated type
// Return a set of all criteria
// String number Boolean matches any then return the combined type of the three
type res = MyType<string | number | boolean>
Copy the code
Application scenarios
Exclude from T types that can be assigned to U
type MyType<T, U> = T extends U ? never : T;
// Discard number
type res = MyType<string | number | boolean.number>
Copy the code
Because this operation is common, TS encapsulates Exclude internally
type res = Exclude<string | number | boolean.number>
Copy the code
Extract the type from T that can be assigned to U
TS encapsulates Extract inside
// Get only number and string
type res = Extract<string | number | boolean.number | string>
Copy the code
Remove null and undefined from T
TS encapsulates NonNullable internally
// Accept only one parameter, automatically remove null and undefined
type res = NonNullable<string | null | undefined | boolean>
Copy the code
Gets the return value type of the function
TS encapsulates a ReturnType
// String when the return value type is obtained
type res = ReturnType<() = > string>
Copy the code
Gets the type of a tuple of class constructor arguments
TS encapsulates ConstructorParameters internally
class Person {
constructor(name: string, age: number){}}// Return an element consisting of a constructor argument type res = [string, number]
type res = ConstructorParameters<typeof Person>;
Copy the code
Gets the tuple type of the parameter types of the function
TS encapsulates Parameters internally
function say(name: string,age: number, gender: boolean) {}
// type res = [string, number, boolean]
type res = Parameters<typeof say>
Copy the code
9.5 unknown type
1. What is unknown type
The unknown type is a top-level type added to TS 3.0 and is known as secure any
2. Any type can be assigned to the unknown type
let value:unknown;
value = 123
value = 'abc'
value = false
Copy the code
3. Unknown cannot be assigned to another type without type assertion or type refinement based on control flow
let value1: unknown = 123
let value2: number;
// value2 = value1
value2 = value1 as number; // Either do type assertion
// Type refinement of process control
if(typeof value1 === 'number'){
value2 = value1
}
Copy the code
4. If no type assertion or type refinement based on control flow is performed, no operation can be performed on Unknown
let value1: unknown = 123;
value1++ / / an error
(value1 as number) + +if(typeof value1 === 'number'){
value1++
}
Copy the code
5. Unknown types can only be equal or unequal. Other operations cannot be performed (other operations are meaningless).
let value1: unknown = 123;
let value2: unknown = 123;
console.log(value1 === value2)
console.log(value1 ! == value2)// But tsconfig.json generates an error if it is enabled in strict mode
// console.log(value1 >= value2) // Although no error is reported, it is not recommended
Copy the code
6. Any type that crosses with other types ends up being another type
type MyType = number & unknown; / / number type
type MyType2 = unknown & string; / / type string
Copy the code
Any union type with any other type is of unknown type
type MyType = unknown | string | boolean; // unknown
Copy the code
Never is a subtype of Unknown
type MyType = never exntends unknown ? string : number // string
Copy the code
9. Keyof unknown equals never
type MyType = keyof unknown; // never
Copy the code
10. A value of the unknown type cannot directly access attributes, methods, or create instances
class Person {
name: string;
say():void {
console.log(`name=The ${this.name}`)}}/ / an error
let p: unknown = new Person('aa')
Copy the code
11. When using a mapping type, if the unknown type is traversed, no type is mapped
type MyType<T> = {
[P in keyof T]: any
}
type res = MyType<number> // number
type res2 = MyType<unknown> / / empty
Copy the code
9.6 Symbol type
Same as Symbol in ES6
1. What is Symbol
Symbol is a new data type in ES6 that is divided into base data types.
Basic data types: string, number, Boolean, undefined, NULL, Symbol
Reference data type: Object
2
Used to represent a unique value
3. How do I generate a unique value
let xxx = Symbol(a)Copy the code
4. Why do we need Symbol
In enterprise development, when some third-party frameworks or plug-ins are customized, the attributes or methods of the same name may be added, and the original attributes or methods of the framework may be overwritten. To avoid this, the framework author or we can use Symbol as the name of the property or method
// Suppose this is a plug-in
let obj = {
name: 'alex'
}
// We overwrite the original value because it is the same as the plugin variable name
obj.name = 'tom'
console.log(obj.name) // tom
// In this case, we use Symbol
let name = Symbol(a)let obj = {
// If you want a variable to be the key of an object, you must enclose the [] parentheses
[name]: 'alex'
}
obj.name = '123456'
// There is no overwrite at this time
obj = {
name: '123456'.Symbol(name): 'alex'
}
Copy the code
5. How to distinguish the Symbol
When generating values through Symbol, you can set a tag that is used only for distinction and has no other meaning.
// Name is used as a tag only for differentiation
let name = Symbol('name')
Copy the code
6. Use the Symbol
- Cannot be added to generate Symbol
new
Because this is a base data type, not a reference data type - The Symbol notation is just an easy way to read the code
- Cannot be converted to a numeric value when doing a type conversion
- You can’t do any operations
- The values generated by Symbol must be saved or they will not be used later
- The for loop cannot iterate over the properties and methods of Symbol
Object.getOwnPropertySymbols(obj)
Method to retrieve all Symbol properties or methods in obj
9.7 Iterators and generators
And ES6 iterators and generators are the same
1. Iterator iterator
In ES6 the iterator interface is for… of
By default: Array/Map/Set/String/TypedArray/function of the arguments object/NodeList iterator is realized
Object does not implement the iterator interface by default, so you cannot use for by default… of
let arr = [1.3.5]
for(let value of arr) {
console.log(value)
}
/ / 1
/ / 3
/ / 5
Copy the code
As long as a piece of data implements the Iterator interface, that data has a property called symbol. iterator, which returns a method
let arr = [1.3.4]
let it = arr[Symbol.iterator]();
console.log(it) // Array iterator
There is a method in IT called next
console.log(it.next()) // { value: 1, done: false }
console.log(it.next()) // { value: 3, done: false }
console.log(it.next()) // { value: 4, done: false }
console.log(it.next()) // { value: undefined, done: true }
Copy the code
- As long as a piece of data implements the Iterator interface, there will be one
Symbol.iterator
attribute - This property returns a function
- The returned function returns an object
- The returned object will have a name called
next
The function of next
Each execution returns an object{value: 1, done: false}
Implement iterator manually
class MyArray {
constructor() {
for (let i = 0; i < arguments.length; i++) {
this[i] = arguments[i]
}
this.length = arguments.length
}
[Symbol.iterator]() {
let index = 0
let _this = this
return {
next() {
if (index < _this.length) {
return {
value: _this[index++],
done: false,}}else {
return {
value: _this[index],
done: true,}}},}}}let arr = new MyArray(1.3.4)
for (const value of arr) {
console.log(value)
}
Copy the code
Application scenarios of Iterator
Deconstructing assignment:
let arr = [1.3]
let [x, y, z] = arr
console.log(x, y, z) // 1, 3, undefined
Copy the code
The interior is actually deconstructed using the next() method
Extended operator
let arr1 = [1.2]
let arr2 = [3.4]
let arr3 = [5.6]
let arr4 = [...arr1, ...arr2, ...arr3] // 1, 2, 3, 4, 5, 6
Copy the code
2. Generator
1 the generator concept
What is the generator
The Generator function is an asynchronous programming solution provided by ES6. The Generator function can encapsulate multiple states internally and therefore can be understood as a state machine.
How to define generator
Just add * to function after normal functions
function* generator() {}Copy the code
The difference between a generator and a normal function
- When a generator is called, an iterator object is returned whether or not the function returns a value
- When a generator function is called, the code encapsulated in the function is not executed immediately
The yield keyword
What really makes generator valuable is the yield keyword
- Used inside generator functions
yield
The keyword defines the state - Yield allows the logic inside the generator to slice multiple states
- By calling the iterator
next
Method executes a partial code
function* gen() {
// Use yield to define a state, which cuts up part of the code
console.log('123')
yield 'aaa'
console.log('456')
yield 1 + 1
console.log('789')
yield true
}
Copy the code
Each execution of next executes the next piece of code
const g = gen()
console.log(g.next()) // 123 { value: 'aaa', done: false }
console.log(g.next()) // 456 { value: 2, done: false }
console.log(g.next()) // 789 { value: true, done: false }
console.log(g.next()) // { value: undefined, done: true }
Copy the code
When you call next, you can pass a parameter that will be passed to the last yield
function* gen() {
console.log('First code')
let res = yield '123'
console.log('Second code:', res)
yield 1 + 1
console.log('Third code')
yield true
}
const g = gen()
g.next() // The first code
g.next('Get parameters') // Get the parameters
g.next() // The third code
Copy the code
2 Application scenarios of the Generator
Let the function return multiple values
// If you want the difference of the sum of two numbers, you can do this before
function calculate(a, b) {
return [a + b, a - b]
}
// Use generator to do so
function* calculate(a, b) {
yield a + b
yield a - b
}
Copy the code
Use generator to quickly define an iterator on an object
let obj = {
name: 'alex'.age: 19.gender: 'male',
}
obj[Symbol.iterator] = function* () {
let keys = Object.keys(this)
for (let i = 0; i < keys.length; i++) {
yield obj[keys[i]]
}
}
let it = obj[Symbol.iterator]()
for (const value of obj) {
console.log(value)
}
// alex 19 male
Copy the code
9.8 Module System
1. The ES6 module
Separate import and Export
import xxx;
import { xxx } from 'xxx'
Copy the code
One-time import/Export
import { xxx, yyy, zzz } from 'aaa'
export { xxx, yyy, zzz }
Copy the code
Default Import/Export
import xxx from 'aaa'
export default xxx
Copy the code
2. The Node module
export
exports.xxx = xxx
module.exports.xxx = xxx
Copy the code
The import
const xxx = require('yyy')
const { xxx, yyy } = require('zzz')
Copy the code
3. The TS compatible
ES6 modules are incompatible with Node modules, so TS was introduced for compatibility
export = xxx;
import xxx = require('yyy')
Copy the code
9.9 Namespaces
1. What is a namespace
A namespace can be thought of as a tiny module that we can use when we first write related business code together without contaminating the global space. The essence is to define a large object, the variable/method/class/interface… Put it inside.
2. Differences between namespaces and modules
- Namespaces can be used to encapsulate and prevent global contamination of code used within a program
- Code that is used outside the program can be encapsulated and protected from global contamination using modules
- Summary: Since modules can do the same thing, most of the time just use modules
namespace Validation {
const letterRegexp = /^[A-Za-z]+$/
// It needs to be exported to be used in the outside world
export const LetterValidator = value= > {
return letterRegexp.test(value)
}
}
// Use a function in the namespace
Validation.LetterValidator('aaa') // true
Copy the code
3. Encapsulate the namespace into a module
// ./test/test.ts
namespace Validation {
const letterRegexp = /^[A-Za-z]+$/
// It needs to be exported to be used in the outside world
export const LetterValidator = value= > {
return letterRegexp.test(value)
}
}
Copy the code
// To use it, you must add the following three slashes
/// <reference path="/test/test.ts" />
// Use a function in the namespace
Validation.LetterValidator('aaa')
Copy the code
TS encounters ///
Tsconfig. json outFile indicates the packaged output file, and outDir indicates the output directory
{
"module": "system"."outFile": "./js/bundle.js"."outDir": "./dist"
}
Copy the code
9.10 Declaring a Merger
Interfaces and namespaces are repeatable in TS, and TS consolidates multiple namesakes into one.
1. The interface
- Interface with the same name If the attribute name is the same, the attribute type must be the same
- An interface with the same name is an overload of a function
2. Namespace
- A namespace with the same name cannot have properties or methods with the same name
- The namespace of the same name did not pass any other namespace
export
The exported content is not available
3. Namespace merges with classes/functions/enumerations of the same name
Note: The class must be defined before the namespace
The namespace exported method is merged into the class as a static method
class Person {
say(): void {
console.log('hello world')}}namespace Person {
export const hi = () = > {
console.log('hi')}}Copy the code
4. Merge namespaces and functions
Note: functions must be declared before the namespace
function getCounter() {
getCounter.count++
console.log(getCounter.count)
}
namespace getCounter {
export let count: number = 0
}
getCounter(); / / 1
getCounter(); / / 2
Copy the code
5. Namespace and enumeration merge
Note: There is no order of precedence
enum Gender {
Male,
Female
}
namespace Gender {
export const A: number = Awesome!
}
console.log(Gender)
Copy the code
9.11 a decorator
1. What are decorators
Decorators are a new syntax in ES7 and are currently part of the proposal.
A decorator is a special type of declaration that can be attached to a class, method, accessor, property, or parameter. Decorators that are added to different places have different names or features.
2. Basic format of decorator
- Plain decorator
- Decoration factory
- Decorator combination
3. How to use decorators in TS
In tsconfig.json, add the experimentalDecorators configuration
{
"experimentalDecorators": true
}
Copy the code
// Test is a plain decorator
function test(target) {
console.log('hello')}// Bind a decorator to Person
The decorator code is executed before the class is defined
@test
class Person {}Copy the code
Decoration factory
The decorator that returns a function is the decorator factory
function factory() {
console.log('factory out')
return target= > {
console.log('factory in')}}// Regular decorator and decorator factory can be mixed
// Order of execution: Execute decorator factory first, then normal decorator
@test
@factory(a)class Person {} // Out then in
Copy the code
- Execute all decorator factories from top to bottom, and then execute all normal decorators from bottom to top
function f1() {
console.log('f1 out')
return target= > {
console.log('f1 in')}}function f2() {
console.log('f2 out')
return target= > {
console.log('f2 in')}}function n1() {
console.log('n1 in')}function n2() {
console.log('n2 in')}@n1
@f1(a)@f2(a)@n2
class Person {}
// Output sequence:
// f1 out / f2 out / n2 in / f2 in / f1 in / n1 in
Copy the code
4. Class decorator
- The class decorator is bound to the class declaration (next to it)
- Class decorators are used to listen to, modify, or replace class definitions
- When executing a class decorator function, the bound class is passed to the decorator as its only argument
- If the class decorator returns a new class, it replaces the original class definition with the new class
Class decorator and decorator factory
Class decorators can pass parameters
function test<T extends {new(... args:any[]): {}}>(target: T) {
return class extends target {
name: string = 'alex'
age: number = 19}}@test
class Person {}
let p = new Person()
console.log(p) // { name: alex, age: 19 }
Copy the code
Method decorators
Method decorators written before a method declaration are used to monitor, modify, or replace method definitions.
The method decorator expression is called as a function at run time, passing in the following three arguments:
- A static method is the current class, and an instance method is the current instance
- The name of the bound method
- The property descriptor of the bound method
// A case in point: dynamic replacement methods
function test(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.value = (): void= > {
console.log('decorator name')}}class Person {
@test
sayName() :void {
console.log('inner name')}}const p = new Person()
p.sayName() // decorator name
Copy the code
Accessor decorator
Accessor decorator lives before an accessor declaration (next to the accessor declaration)
Accessor decorators are applied to accessor property descriptors and can be used to monitor, modify, or replace the definition of an accessor.
Accessor decorator expressions are called as functions at run time, passing in the following three arguments:
- Constructor of the class for static members and prototype object of the class for instance members
- Name of member
- Attribute descriptor for a member
Note:
TypeScript does not allow you to decorate both get and set accessors for a member. Instead, all accessors for a member must be applied to the first accessor in the document order
function test(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// We can get both get and set, so only one accessor decorator can be set
descriptor.set = (value: string) = > {
target.myName = value
}
descriptor.get = (): string= > {
return target.myName
}
}
class Person {
private _name: string;
constructor(name: string) {
this._name = name
}
// The first decorator is set to either set or get
@test
get name() :string {
return this._name
}
set name(value: string) {
this._name = value
}
}
const p = new Person("zzz")
p.name = 'yyy'
console.log(p.name)
Copy the code
7. Property decorator
Property decorator lives before a property declaration (right next to the property declaration)
The property decorator expression is called as a function at run time, passing in the following three arguments:
- For static attributes it is the current class, for instance attributes it is the current instance
- Name of member
function test(target: any, propertyKey: string) {
target[propertyKey] = '123'
}
class Person {
static age: number;
@testname? :string;
}
Copy the code
8. Parameter decorators
Parameter decorator lives before a parameter declaration (right next to the parameter declaration)
The parameter decorator expression is called as a function at run time, passing in the following three arguments:
- This is the current class for static members and the current instance for instance members
- The name of the method in which the parameter is located
- The index of a parameter in the parameter list
function test(target: any, propertyKey: string, index: number) {}class Person {
say(age: number.@test name: string) :void{}}Copy the code
9. Pay attention to
The most common use of attribute and parameter decorators is reflect-metadata, which adds additional information without changing the original data.
However, metadata is also currently in the proposal and is not included in the formal specification
9.12 with
1. Object mixing
let obj1 = { name: 'alex' }
let obj2 = { age: 13 }
Object.assign(obj1, obj2)
console.log(obj1) // { name: 'alex', age: 13 }
console.log(obj2) // { age" 13 }
Copy the code
2. Class
You need to customize a method to mixin
function myMixin(target: any.from: any[]) {
from.forEach(fromItem= > {
Object.getOwnPropertyNames(fromItem.prototype).forEach(name= > {
target.prototype[name] = fromItem.prototype[name]
})
})
}
class Cat {
name: string
say(): void {
console.log('meow')}}class Dog {
age: number
run(): void {
console.log('run')}}class Animal implements Cat.Dog {
name: string
say: () = > void
age: number
run: () = > void
}
myMixin(Animal, [Dog, Cat])
let a = new Animal()
console.log(a)
a.run()
a.say()
Copy the code
9.13 the statement
1. What is a statement
In enterprise development we inevitably need to introduce third-party JS libraries, but by default TS does not know the JS libraries we introduce. So when we use these JS libraries, we need to tell TS what it is and how to use it.
$TS does not know what it is and does not give any hint
// declare $, which takes a selector, as a function
declare const $: (selector: string) = > {
// Write the attributes/methods that exist in this declaration
width(): number,
height(): number,
ajax(url: string.config: {}) :void;
}
console.log($)
// There is a hint
$('.app').width()
Copy the code
2. Declaration document
TS recommends writing the definition in a separate file, usually called xx.d. TS
Note: Implementations cannot appear in declarations
// test.d.ts
declare let name: string;
declare function say(name: string) :void;
declare class Person {
name: string
age: number
constructor(name: string, age: number)
say() :void
}
Copy the code
// test.ts
let name: string = 'aa'
function say(name: string) {
console.log(name)
}
Copy the code
3. Declare the installation
For common third-party libraries, many already have a ready-made declaration file, so in enterprise development, we can directly use the third-party type declaration file to use
Specification of TS declaration file @types/xx
For example, IF I want to use jquery declaration file, directly
npm install @types/jquery
Copy the code
/ / use
import jQuery = require('jquery')
// There is a hint
jQuery().width()
Copy the code
If the third party library you are using does not prompt you, you have to follow the template file to write your own