Have you mastered these 10 knowledge points of TS?
The day before yesterday
The following article comes from the way of cultivating immortals in the whole stack , Author a Baoge
The whole stack is the way to cultivate immortals
In object-oriented language, class is the construction of an object-oriented computer programming language, the blueprint for creating objects, and describes the common attributes and methods of the created objects.
1, Class properties and methods
1.1 member properties and static properties of class
In TypeScript, we can class Keyword to define a class:
class Person { name: string; // Member properties constructor(name: string) { // Class constructor this.name = name; } }
In the above code, we use class Keyword defines a Person Class that contains a name Member properties for. In fact, the class in TypeScript is a syntax sugar (the so-called syntax sugar changes a writing method on the basis of a previous syntax, and the functions are the same, but the writing methods are different, mainly to make it easier for developers to understand during use). If the compilation target is set to ES5 The following code will be generated:
"use strict"; var Person = /** @class */ (function () { function Person(name) { this.name = name; } return Person; }());
Class can not only define member properties, but also static Keyword to define static attributes:
class Person { static cid: string = "exe"; name: string; // Member properties constructor(name: string) { // Class constructor this.name = name; } }
So what is the difference between member attributes and static attributes? Before answering this question, let's take a look at the compiled ES5 code:
"use strict"; var Person = /** @class */ (function () { function Person(name) { this.name = name; } Person.cid = "exe"; return Person; }());
From the above code, we can see that the member attribute is defined on the instance of the class, while the static attribute is defined on the constructor.
1.2 member methods and static methods of classes
In TS class, we can define not only member attributes and static attributes, but also member methods and static methods, as shown below:
class Person { static cid: string = "exe"; name: string; // Member properties static printCid() { // Define static methods console.log(Person.cid); } constructor(name: string) { // Class constructor this.name = name; } say(words: string) :void { // Define member methods console.log(`${this.name} says: ${words}`); } }
So what's the difference between member methods and static methods? Again, before answering this question, let's take a look at the compiled ES5 code:
"use strict"; var Person = /** @class */ (function () { function Person(name) { this.name = name; } Person.printCid = function () { console.log(Person.cid); }; Person.prototype.say = function (words) { console.log(this.name + " says\uFF1A" + words); }; Person.cid = "exe"; return Person; }());
As can be seen from the above code, the member method will be added to the prototype object of the constructor, and the static method will be added to the constructor.
1.3 class member method overloading
Function overloading or method overloading is the ability to create multiple methods with the same name and different parameter numbers or types. When defining the member method of a class, we can also overload the member method:
class Person { constructor(public name: string) {} say(): void; say(words: string): void; say(words?: string) :void { // Method overloading if(typeof words === "string") { console.log(`${this.name} says: ${words}`); } else { console.log(`${this.name} says: Nothing`); } } } let p1 = new Person("Semlinker"); p1.say(); p1.say("Hello TS");
If you want to learn more about function overloading, you can continue to read It's time to show the real technology - TS separation This is an article.
2, Accessor
In TypeScript, we can getter and setter Method to realize data encapsulation and validity verification to prevent abnormal data.
let passcode = "Hello TypeScript"; class Employee { private _fullName: string = ""; get fullName(): string { return this._fullName; } set fullName(newName: string) { if (passcode && passcode == "Hello TypeScript") { this._fullName = newName; } else { console.log("Error: Unauthorized update of employee!"); } } } let employee = new Employee(); employee.fullName = "Semlinker";
In the above code, for private _ fullName Property, we provide it externally getter and setter To control the access and modification of this property.
3, Class inheritance
Inheritance is a hierarchical model that connects classes. It refers to the ability of a class (called subclass or sub interface) to inherit the functions of another class (called parent class or parent interface) and add its own new functions. Inheritance is the most common relationship between classes or interfaces. Through class inheritance, we can realize code reuse.
Inheritance is a is-a Relationship:
In TypeScript, we can extends Keyword to implement class inheritance:
3.1 parent class
class Person { constructor(public name: string) {} public say(words: string) :void { console.log(`${this.name} says: ${words}`); } }
Subclass 3.2
class Developer extends Person { constructor(name: string) { super(name); this.say("Learn TypeScript") } } const p2 = new Developer("semlinker"); // Output: "semlinker says: Learn TypeScript"
because Developer Class inherits Person Class, so we can Developer Call in the constructor of class say method. It should be noted that it is used in TypeScript extends Only a single class can be inherited when:
class Programmer {} // Classes can only extend a single class.(1174) class Developer extends Person, Programmer { constructor(name: string) { super(name); this.say("Learn TypeScript") } }
Although only single inheritance is allowed in TypeScript, it allows us to implement multiple interfaces. Specific use examples are as follows:
interface CanSay { say(words: string) :void } interface CanWalk { walk(): void; } class Person implements CanSay, CanWalk { constructor(public name: string) {} public say(words: string) :void { console.log(`${this.name} says: ${words}`); } public walk(): void { console.log(`${this.name} walk with feet`); } }
In addition, in addition to inheriting specific implementation classes, we can also inherit abstract classes when implementing inheritance.
4, Abstract class
use abstract The class declared by keyword is called abstract class. An abstract class cannot be instantiated because it contains one or more abstract methods. The so-called abstract method refers to a method that does not contain a specific implementation:
abstract class Person { constructor(public name: string){} abstract say(words: string) :void; } // Cannot create an instance of an abstract class.(2511) const lolo = new Person(); // Error
Abstract classes cannot be instantiated directly. We can only instantiate subclasses that implement all abstract methods. The details are as follows:
class Developer extends Person { constructor(name: string) { super(name); } say(words: string): void { console.log(`${this.name} says ${words}`); } } const lolo = new Developer("lolo"); lolo.say("I love ts!"); // Output: lolo says I love ts!
5, Class access modifier
In TS type, we can use public,protected or private To describe the visibility of such properties and methods.
5.1 public
public Modified attributes or methods are public and can be accessed anywhere. By default, all attributes or methods are public:
class Person { constructor(public name: string) {} public say(words: string) :void { console.log(`${this.name} says: ${words}`); } }
5.2 protected
protected Modified properties or methods are protected, which is similar to private, except that protected members can still be accessed in derived classes.
class Person { constructor(public name: string) {} public say(words: string) :void { console.log(`${this.name} says: ${words}`); } protected getClassName() { return "Person"; } } const p1 = new Person("lolo"); p1.say("Learn TypeScript"); // Ok // Property 'getClassName' is protected and only accessible within class 'Person' and its subclasses. p1.getClassName() // Error
It can be seen from the above error information that protected Modifier decorated methods can only be used in the current class or its subclasses.
class Developer extends Person { constructor(name: string) { super(name); console.log(`Base Class: ${this.getClassName()}`); } } const p2 = new Developer("semlinker"); // Output: "Base Class: Person"
5.3 private
private The modified property or method is private and can only be accessed inside the class.
class Person { constructor(private id: number, public name: string) {} } const p1 = new Person(28, "lolo"); // Property 'id' is private and only accessible within class 'Person'.(2341) p1.id // Error p1.name // OK
It can be seen from the above error information that private The property modified by the modifier can only be accessed inside the current class. But is that true? In fact, this is just a hint from the TS type checker. We can still access it at runtime Person Instance id Properties. If you don't believe it, let's take a look at the compiled version ES5 code:
"use strict"; var Person = /** @class */ (function () { function Person(id, name) { this.id = id; this.name = name; } return Person; }()); var p1 = new Person(28, "lolo");
5.4 private fields
To solve the above problems, the TypeScript team has supported it since version 3.8 ECMAScript private field is used as follows:
class Person { #name: string; constructor(name: string) { this.#name = name; } } let semlinker = new Person("semlinker"); // Property '#name' is not accessible outside class 'Person' because it has a private identifier. semlinker.#name // Error
that ECMAScript private field Follow private What's special about modifiers? Here, let's take a look at the generated by compilation ES2015 code:
"use strict"; var __classPrivateFieldSet = // Omit relevant codes var _Person_name; class Person { constructor(name) { _Person_name.set(this, void 0); __classPrivateFieldSet(this, _Person_name, name, "f"); } } _Person_name = new WeakMap(); let semlinker = new Person("Semlinker");
From the above results, we can see that the newly added in ES2015 is used when processing private fields WeakMap data type. If you don't know about WeakMap, you can read it You don't know the WeakMap This article. Let's summarize that private fields and general properties (even using private Modifier declared attributes) differ:
-
Private field with # Character, sometimes we call it private name;
-
Each private field name is uniquely limited to the class it contains;
-
TypeScript accessibility modifiers (such as public or private) cannot be used on private fields;
-
Private fields cannot be accessed outside the contained class or even detected.
6, Class expression
TypeScript 1.6 adds support for ES6 class expressions. Class expression is a syntax used to define a class. Like function expressions, class expressions can be named or anonymous. If it is a named class expression, the name can only be accessed inside the class body.
The syntax of class expression is as follows ([] square brackets indicate that it is optional):
const MyClass = class [className] [extends] { // class body };
Based on the syntax of class expression, we can define a Point class:
let Point = class { constructor(public x: number, public y: number) {} public length() { return Math.sqrt(this.x * this.x + this.y * this.y); } } let p = new Point(3, 4); console.log(p.length()); // Output: 5
It should be noted that when using class expressions to define classes, we can also use extends keyword. Space is limited, so I won't introduce it here. Interested partners can test it by themselves.
7, Generic class
Using generics in a class is also very simple. We just need to use after the class name < T, ...> The syntax of defines any number of type variables. Specific examples are as follows:
class Person<T> { constructor( public cid: T, public name: string ) {} } let p1 = new Person<number>(28, "Lolo"); let p2 = new Person<string>("exe", "Semlinker");
Next, we instantiate p1 As an example, let's analyze the processing process:
-
In instantiation Person Object, we pass in number Type and corresponding construction parameters;
-
Later in Person Class, type variable T The value of becomes number Type;
-
Last constructor cid The parameter type will also become number Type.
I believe some readers will have questions here. When do we need to use generics? Generally, when deciding whether to use generics, we have the following two reference standards:
-
When your function, interface or class will handle multiple data types;
-
When a function, interface, or class uses the data type in multiple places.
8, Construct signature
In the TypeScript interface, you can use new Keyword to describe a constructor:
interface Point { new (x: number, y: number): Point; }
In the above interfaces new (x: number, y: number) We call it constructive signature, and its syntax is as follows:
ConstructSignature:new TypeParametersopt ( ParameterListopt ) TypeAnnotationopt
In the above construction signature, TypeParametersopt , ParameterListopt and TypeAnnotationopt It represents optional type parameters, optional parameter list and optional type annotation respectively. So what's the use of understanding constructive signatures? Here's an example:
interface Point { new (x: number, y: number): Point; x: number; y: number; } class Point2D implements Point { readonly x: number; readonly y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } const point: Point = new Point2D(1, 2); // Error
For the above code, the TypeScript compiler (v4.4.3) will prompt the following error message:
Type 'Point2D' is not assignable to type 'Point'. Type 'Point2D' provides no match for the signature 'new (x: number, y: number): Point'.
To solve this problem, we need to Point Interface separation:
interface Point { x: number; y: number; } interface PointConstructor { new (x: number, y: number): Point; }
After splitting the interface, in addition to the previously defined Point2D Class, we define another newPoint Factory function, which is used to PointConstructor Type to create the corresponding point object.
class Point2D implements Point { readonly x: number; readonly y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } function newPoint( // Factory method pointConstructor: PointConstructor, x: number, y: number ): Point { return new pointConstructor(x, y); } const point: Point = newPoint(Point2D, 1, 2);
9, Abstract construction signature
Abstract construction signature is introduced in typescript version 4.2 to solve the following problems:
type ConstructorFunction = new (...args: any[]) => any; abstract class Utilities {} // Type 'typeof Utilities' is not assignable to type 'ConstructorFunction'. // Cannot assign an abstract constructor type to a non-abstract constructor type. let UtilityClass: ConstructorFunction = Utilities; // Error.
From the above error information, we can not assign Abstract constructor types to non Abstract constructor types. To solve this problem, we need to use abstract Modifier:
declare type ConstructorFunction = abstract new (...args: any[]) => any;
It should be noted that for abstract constructor types, we can also pass in specific implementation classes:
declare type ConstructorFunction = abstract new (...args: any[]) => any; abstract class Utilities {} class UtilitiesConcrete extends Utilities {} let UtilityClass: ConstructorFunction = Utilities; // Ok let UtilityClass1: ConstructorFunction = UtilitiesConcrete; // Ok
For versions below TypeScript 4.2, we can solve the above problems in the following ways:
type Constructor<T> = Function & { prototype: T } abstract class Utilities {} class UtilitiesConcrete extends Utilities {} let UtilityClass: Constructor<Utilities> = Utilities; let UtilityClass1: Constructor<UtilitiesConcrete> = UtilitiesConcrete;
After introducing the abstract construction signature, let's briefly introduce it at last Class type and typeof class type The difference.
10, Class type and typeof class type
class Person { static cid: string = "exe"; name: string; // Member properties static printCid() { // Define static methods console.log(Person.cid); } constructor(name: string) { // Class constructor this.name = name; } say(words: string) :void { // Define member methods console.log(`${this.name} says: ${words}`); } } // Property 'say' is missing in type 'typeof Person' but required in type 'Person'. let p1: Person = Person; // Error let p2: Person = new Person("Semlinker"); // Ok // Type 'Person' is missing the following properties from type 'typeof Person': prototype, cid, printCid let p3: typeof Person = new Person("Lolo"); // Error let p4: typeof Person = Person; // Ok
By observing the above code, we can draw the following conclusions:
-
When used Person Class as a type, the value of the constraint variable must be Person Class;
-
When used typeof Person As a type, the value of a variable that can be constrained must contain static properties and methods on the class.
In addition, it should be noted that TypeScript uses Structured Type system, which is similar to that adopted by Java/C + + Nominalization The type system is different, so the following codes can work normally in TS:
class Person { constructor(public name: string) {} } class SuperMan { constructor(public name: string) {} } let p1: SuperMan = new Person("Semlinker"); // Ok
OK, in daily work, I'll introduce the common knowledge of TypeScript class here. Thank you for sharing!