Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the strange symbols in TypeScript

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains "what strange symbols are there in TypeScript". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what strange symbols are there in TypeScript"?

One! Non-null assertion operator

A new suffix expression operator in the context when the type checker cannot determine the type! Can be used to assert that operands are of non-null and non-undefined types. Specifically, x! Null and undefined are excluded from the x range.

So what is the use of non-empty assertion operators? Let's first take a look at some scenarios where non-empty assertion operators are used.

1.1 ignore undefined and null types

Function myFunc (maybeString: string | undefined | null) {/ / Type 'string | null | undefined' is not assignable to type' string'. / / Type 'undefined' is not assignable to type' string'. Const onlyString: string = maybeString; / / Error const ignoreUndefinedAndNull: string = maybeStringstones; / / Ok}

1.2 ignore the undefined type when calling a function

Type NumGenerator = () = > number; function myFunc (numGenerator: NumGenerator | undefined) {/ / Object is possibly 'undefined'. (2532) / / Cannot invoke an object which is possibly' undefined'. (2722) const num1 = numGenerator (); / / Error const num2 = numGenerator! (); / / OK}

Because! The non-null assertion operator is removed from the compiled JavaScript code, so be careful when actually using it. For example, the following example:

Const a: number | undefined = undefined; const b: number = aversion; console.log (b)

The above TS code compiles and generates the following ES5 code:

"use strict"; const a = undefined; const b = a; console.log (b)

Although in the TS code, we use non-empty assertions so that the const b: number = await; statement can be checked by the TypeScript type checker. But in the generated ES5 code,! The non-null assertion operator has been removed, so execute the above code in the browser and output undefined on the console.

? Continue to read: introduction?. And?? Operator, one more! Non-null assertion operator

Second,?. Operator

TypeScript 3.7implements one of the most popular ECMAScript functions: Optional Chaining. With the optional chain in place, if we encounter null or undefined when writing code, we can immediately stop some expressions from running. The core of the optional chain is new. Operator, which supports the following syntax:

Obj?.prop obj?. [expr] arr?. [index] func?. (args)

Here we give an example of optional property access:

Const val = axi.b

To better understand the optional chain, let's take a look at the ES5 code compiled by the const val = axi.b statement:

Var val = a = null | | a = void 0? Void 0: a.b

The above code automatically checks whether the object an is null or undefined, and if so, returns undefined immediately, so that some expressions can be stopped immediately. You may have thought of using it?. To replace a lot of code that uses & & to perform null checks:

If (a & & a.b) {} if (aq.b) {} / * if (aq.b) {} compiled ES5 code * * if (* a = null | | a = void 0 *? Void 0: A.B) {*} * /

But it is important to note that?. Slightly different from the behavior of the & & operator, & & is designed to detect falsy values, such as empty strings, 0, NaN, null, and false. And?. Only validates whether the object is null or undefined, and there is no "short circuit" for 0 or empty strings.

2.1 optional element access

In addition to supporting the access of optional attributes, the optional chain also supports the access of optional elements, which behave like the access of optional attributes, except that the access of optional elements allows us to access attributes that are not identifiers, such as arbitrary strings, numeric indexes, and Symbol:

Function tryGetArrayElement (arr?: T [], index: number = 0) {return arr?. [index];}

The above code is compiled to generate the following ES5 code:

"use strict"; function tryGetArrayElement (arr, index) {if (index = void 0) {index = 0;} return arr = null | | arr = = void 0? Void 0: arr [index];}

By observing the generated ES5 code, it is obvious that the tryGetArrayElement method automatically detects whether the value of the input parameter arr is null or undefined, thus ensuring the robustness of our code.

2.2 optional chains and function calls

You can also use an optional chain when trying to call a method that may not exist. This is very useful in the actual development process. A method is not available in the system, which may be due to inconsistent versions or user device compatibility issues. If the called method does not exist when a function is called, you can use an optional chain to make the expression automatically return undefined instead of throwing an exception.

Optional calls are also easy to use, such as:

Let result = obj.customMethod?. ()

The TypeScript code is compiled to produce the following ES5 code:

Var result = (_ a = obj.customMethod) = null | | _ a = void 0? Void 0: _ a.call (obj)

In addition, when using optional calls, we should pay attention to the following two considerations:

If there is a property name and the value corresponding to the attribute name is not a function type, use?. A TypeError exception is still generated.

The operation behavior of optional chains is limited to access to attributes, calls, and elements-- it does not extend to subsequent expressions, that is, optional calls do not prevent division operations in axi.b / someMethod () expressions or method calls to someMethod.

? Read on: what should I do if I encounter the deep attributes of the access object? Learn about the optional chain!

Third,? Null merge operator

In addition to the introduction of the optional chain described earlier in TypeScript 3.7, In addition, a new logical operator, the null merging operator, is introduced. When the left Operand is null or undefined, it returns the right Operand, otherwise it returns the left Operand.

Unlike the logic or | | operator, logic may return the right Operand when the left Operand is a falsy value. That is, if you use "|" to set default values for certain variables, you may encounter unexpected behavior. For example, when it is a falsy value ('', NaN, or 0).

Here's a concrete example:

Const foo = null? 'default string'; console.log (foo); / / output: "default string" const baz = 0? 42; console.log (baz); / / output: 0

When the above TS code is compiled, the following ES5 code is generated:

"use strict"; var _ a, _ b; var foo = (_ a = null)! = = null & & _ a! = = void 0? _ a: 'default string'; console.log (foo); / / output: "default string" var baz = (_ b = 0)! = = null & & b! = = void 0? B: 42; console.log (baz); / / output: 0

By looking at the above code, we have a more intuitive understanding of how the null merge operator solves the potential problems with the previous | | operator. Let's introduce the features of the null merge operator and some considerations when using it.

3.1 short circuit

When the left expression of the null merge operator is not null or undefined, the right expression is not evaluated.

Function A () {console.log ('A was called'); return undefined;} function B () {console.log ('B was called'); return false;} function C () {console.log ('C was called'); return "foo";} console.log (A ()? C (); console.log (B ())? C ())

After the above code runs, the console outputs the following results:

A was called C was called foo B was called false

3.2 cannot be shared with & & or | | operator

If null merge operator? Used directly with the AND (& &) and OR (| |) operators. It won't work. In this case, a SyntaxError is thrown.

/ /'| | 'and'?' Operations cannot be mixed without parentheses. (5076) null | | undefined? "foo"; / / raises a SyntaxError / /'& & 'and'?' Operations cannot be mixed without parentheses. (5076) true & & undefined? "foo"; / / raises a SyntaxError

However, it is possible to use parentheses to explicitly indicate priority, such as:

(null | | undefined)? "foo"; / / returns "foo"

3.3 with the optional chain operator?. The relationship between

Null merge operator for undefined and null values, optional chain operator?. It's the same thing. The optional chain operator is useful for accessing objects whose properties may be undefined and null.

Interface Customer {name: string; city?: string;} let customer: Customer = {name: "Semlinker"}; let customerCity = customer?.city?? "Unknown city"; console.log (customerCity); / / output: Unknown city

We have already introduced the application scenario and some considerations when using the null merging operator, which can not only be used in TypeScript 3.7 or later. Of course, you can also use it in the JavaScript environment, but you need to use Babel to support the null merge operator in version 7.8.0 of Babel.

4. Optional attributes

In object-oriented languages, interface is a very important concept, it is the abstraction of behavior, and the concrete how to act needs to be implemented by the class. The interface in TypeScript is a very flexible concept, which can be used not only to abstract part of the behavior of the class, but also to describe the "Shape" of the object.

Using the interface keyword in TypeScript, you can declare an interface:

Interface Person {name: string; age: number;} let semlinker: Person = {name: "semlinker", age: 33,}

In the above code, we declare the Person interface, which contains two required attributes, name and age. When initializing a Person type variable, if an attribute is missing, the TypeScript compiler prompts for the appropriate error message, such as:

/ / Property 'age' is missing in type' {name: string;} 'but required in type' Person'. (2741) let lolo: Person = {/ / Error name: "lolo"}

To solve the above problem, we can declare a property as optional:

Interface Person {name: string; age?: number;} let lolo: Person = {name: "lolo"}

4.1 tool Typ

4.1.1 Partial

In order to improve the code reuse rate in the actual project development process, we can use TypeScript's built-in tool type Partial to quickly make the properties defined in an interface type optional:

Interface PullDownRefreshConfig {threshold: number; stop: number;} / * type PullDownRefreshOptions = {* threshold?: number | undefined; * stop?: number | undefined; *} * / type PullDownRefreshOptions = Partial

Do you think Partial is convenient? let's take a look at how it is implemented:

/ * Make all properties in T optional * / type Partial = {[P in keyof T]?: t [P];}

4.1.2 Required

Since you can quickly declare all the properties defined in an interface as optional, can you make all optional properties mandatory? The answer is yes. To meet this requirement, we can use the Required tool type, which is used as follows:

Interface PullDownRefreshConfig {threshold: number; stop: number;} type PullDownRefreshOptions = Partial / * type PullDownRefresh = {* threshold: number; * stop: number; *} * / type PullDownRefresh = Required

Again, let's take a look at how the Required tool type is implemented:

/ * Make all properties in T required * / type Required = {[P in keyof T] -?: t [P];}

Originally inside the Required tool type, through -? Remove the? from the optional attribute, making the attribute optional to required.

? Keep reading: master these tool types of TS, so that you can get twice the result with half the effort.

5. & operator

In TypeScript, crossing types is the merging of multiple types into a single type. The & operator allows you to stack existing types together to form a type that contains all the types of features you need.

Type PartialPointX = {x: number;}; type Point = PartialPointX & {y: number;}; let point: Point = {x: 1, y: 1}

In the above code, we define the PartialPointX type, then use the & operator to create a new Point type that represents a point with x and y coordinates, and then define a variable of type Point and initialize it.

5.1 merging of base type attributes of the same name

So now the problem is, suppose that in the process of merging multiple types, it happens that some types have the same members, but the corresponding types are not the same, such as:

Interface X {c: string; d: string;} interface Y {c: number; e: string} type XY = X & Y; type YX = Y & X; let p: XY; let q: YX

In the above code, interface X and interface Y both contain the same member c, but their types are not consistent. In this case, can the type of member c in the XY type or YX type be string or number? For example, the following example:

P = {c: 6, d: "d", e: "e"}

Q = {c: "c", d: "d", e: "e"}

Why does the type of member c become never when interface X and interface Y are mixed? This is because the type of member c after mixing is string & number, that is, the type of member c can be either string type or number type. It is obvious that this type does not exist, so the type of member c is never.

5.2 merging of non-underlying type attributes with the same name

In the above example, it happens that the types of interface X and interface Y's internal member c are both basic data types, so what happens if they are non-basic data types. Let's look at a concrete example:

Interface D {d: boolean;} interface E {e: string;} interface F {f: number;} interface A {x: d;} interface B {x: e;} interface C {x: F;} type ABC = A & B & C; let abc: ABC = {x: {d: true, e: 'semlinker', f: 666}}; console.log (' abc:', abc)

After the above code runs successfully, the console outputs the following results:

As you can see from the figure above, when mixing multiple types, if the same member exists and the member type is a non-basic data type, then it can be merged successfully.

6. | Delimiter

In TypeScript, Union Types means that the value can be one of many types, and the union type uses | to separate each type. Union types are usually used with null or undefined:

Const sayHello = (name: string | undefined) = > {/ *... * /}

In the above example, the type of name is string | undefined means that the value of string or undefined can be passed to the sayHello function.

SayHello ("semlinker"); sayHello (undefined)

In addition, for federated types, you may encounter the following usage:

Let num: 1 | 2 = 1; type EventNames = 'click' |' scroll' | 'mousemove'

The 1, 2, or 'click' in the example is called a literal type and is used to constrain that the value can only be one of several values.

6.1 Type protection

When using federated types, we must try to narrow the type of the current value to the actual type of the current value, and type protection is a means to achieve type narrowing.

Type protection is an expression that performs run-time checks to ensure that the type is within a certain range. In other words, type protection guarantees that a string is a string, although its value can also be a number. Type protection is not completely different from property detection, the main idea is to try to detect properties, methods, or prototypes to determine how to handle values.

Currently, there are four main ways to implement type protection:

6.1.1 in keyword

Interface Admin {name: string; privileges: string [];} interface Employee {name: string; startDate: Date;} type UnknownEmployee = Employee | Admin; function printEmployeeInformation (emp: UnknownEmployee) {console.log ("Name:" + emp.name); if ("privileges" in emp) {console.log ("Privileges:" + emp.privileges);} if ("startDate" in emp) {console.log ("Start Date:" + emp.startDate);}}

6.1.2 typeof keyword

Function padLeft (value: string, padding: string | number) {if (typeof padding = "number") {return Array (padding + 1). Join (") + value;} if (typeof padding = =" string ") {return padding + value;} throw new Error (`Expected string or number, got'$padding}'.`);}

Only two forms of typeof type protection are supported: typeof v = = "typename" and typeof v! = = typename. "typename" must be "number", "string", "boolean" or "symbol". But TypeScript does not prevent you from comparing to other strings, and the language does not recognize those expressions as type protected.

6.1.3 instanceof keyword

Interface Padder {getPaddingString (): string;} class SpaceRepeatingPadder implements Padder {constructor (private numSpaces: number) {} getPaddingString () {return Array (this.numSpaces + 1). Join ("");} class StringPadder implements Padder {constructor (private value: string) {} getPaddingString () {return this.value;} let padder: Padder = new SpaceRepeatingPadder (6); if (padder instanceof SpaceRepeatingPadder) {/ / padder is narrowed to 'SpaceRepeatingPadder'}

6.1.4 Type predicates for custom type protection (type predicate)

Function isNumber (x: any): x is number {return typeof x = "number";} function isString (x: any): x is string {return typeof x = "string";}

? Read on: understand the meaning of union types and cross types in TS

Seven, _ numeric delimiter

TypeScript 2.7brings support for numeric delimiters, as outlined in the numeric separator ECMAScript proposal. For a number literal, you can now group numbers by using an underscore as the separator between them:

Const inhabitantsOfMunich = 1 "464" 301; const distanceEarthSunInKm = 149 "600 million; const fileSystemPermission = 0b111" 111 "000; const bytes = 0b1111_10101011_11110000_00001101

Delimiters do not change the value of numeric literals, but logical grouping makes it easier for people to read numbers at a glance. When the above TS code is compiled, the following ES5 code is generated:

"use strict"; var inhabitantsOfMunich = 1464301; var distanceEarthSunInKm = 149600000; var fileSystemPermission = 504; var bytes = 262926349

7.1 use restrictions

Although the numeric delimiter looks simple, there are still some limitations when using it. For example, you can only add a _ delimiter between two numbers. The following uses are illegal:

/ / Numeric separators are not allowed here. (6188) 3room.141592 / / Error 3.0141592 / / Error / / Numeric separators are not allowed here. (6188) 1_e10 / / Error 1e_10 / / Error / / Cannot find name'_ 126301. (2304) _ 126301 / / Error / / Numeric separators are not allowed here. (6188) 126301 _ / / Error / / Cannot find name 'b111111000. (2304) / / An identifier or keyword cannot immediately follow a numeric literal. 0_b111111000 / / Error / / Numeric separators are not allowed here. (6188) 0b_111111000 / / Error

Of course, you can't use multiple _ delimiters in succession, such as:

/ / Multiple consecutive numeric separators are not permitted. (6189) 123 / / Error

7.2 parsing delimiters

In addition, it should be noted that the following functions for parsing numbers do not support delimiters:

Number ()

ParseInt ()

ParseFloat ()

Let's take a look at a practical example:

Number ('123-456') NaN parseInt ('123-456') 123 parseFloat ('123-456') 123

It is obvious that the above results are not what we expected, so we should pay special attention to dealing with delimiters. Of course, to solve the above problems, it is also very simple to delete non-numeric characters. Here we define a function of removeNonDigits:

Const RE_NON_DIGIT = / [^ 0-9] / gu; function removeNonDigits (str) {str = str.replace (RE_NON_DIGIT,''); return Number (str);}

This function removes non-numeric characters by calling the replace method of the string, as follows:

RemoveNonDigits ('123 to 456') 123456 removeNonDigits ('149600000') 149600000 removeNonDigits ('1407836') 1407836

VIII. Grammar

8.1 TypeScript assertion

Sometimes you come across situations where you know more about a value than TypeScript does. Usually this happens when you know clearly that an entity has a more exact type than its existing type.

Type assertions are a way to tell the compiler, "believe me, I know what I'm doing." Type assertions are like type conversions in other languages, but there is no special data checking and deconstruction. It has no runtime impact, but only works during the compile phase.

Type assertions come in two forms:

8.1.1 Angle bracket syntax

Let someValue: any = "this is a string"; let strLength: number = (someValue) .length

8.1.2 as syntax

Let someValue: any = "this is a string"; let strLength: number = (someValue as string) .length

8.2 TypeScript generics

For readers new to TypeScript generics, it will be strange to see the syntax for the first time. It's nothing special, just like passing parameters, we pass the type we want to use for a particular function call.

Referring to the picture above, when we call identity (1), the Number type is like parameter 1, which populates the type wherever T appears. The internal T in the figure is called a type variable, which is the type placeholder we want to pass to the identity function, and it is assigned to the value parameter to replace its type: in this case T acts as a type, not a specific Number type.

Where T stands for Type and is usually used as the first type variable name when defining generics. But in fact T can be replaced by any valid name. In addition to T, the following is what common generic variables represent:

K (Key): represents the key type in the object

V (Value): represents the value type in the object

E (Element): represents the element type.

In fact, we can not only define one type variable, we can introduce any number of type variables we want to define. For example, we introduce a new type variable U to extend the identity function we defined:

Function identity (value: t, message: U): t {console.log (message); return value;} console.log (identity (68, "Semlinker"))

In addition to explicitly setting values for type variables, a more common practice is to have the compiler automatically select these types, making the code more concise. We can omit the angle brackets completely, such as:

Function identity (value: t, message: U): t {console.log (message); return value;} console.log (identity (68, "Semlinker"))

For the above code, the compiler is smart enough to know our parameter types and assign them to T and U without requiring the developer to specify them explicitly.

9. @ XXX decorator

9.1 decorator syntax

For some of you who are new to TypeScript, you may be surprised to see @ Plugin ({...}) syntax for the first time. In fact, this is the syntax of the decorator, the essence of the decorator is a function, through the decorator we can easily define the metadata related to the object.

@ Plugin ({pluginName: 'Device', plugin:' cordova-plugin-device', pluginRef: 'device', repo:' https://github.com/apache/cordova-plugin-device', platforms: ['Android',' Browser', 'iOS',' macOS', 'Windows'],}) @ Injectable () export class Device extends IonicNativePlugin {}

In the above code, we use the decorator to save the meta-information about the ionic-native plug-in, and the @ symbol in @ Plugin ({...}) is just syntax candy, so why is it syntax candy? Here let's take a look at the compiled ES5 code:

Var _ _ decorate = (this & & this.__decorate) | | function (decorators, target, key, desc) {var c = arguments.length, r = c

< 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >

= 0; iMel -) if (d = decorators [I]) r = (c)

< 3 ? d(r) : c >

3? D (target, key, r): d (target, key) | | r; return c > 3 & & r & & Object.defineProperty (target, key, r), r;}; var Device = / * * @ class * / (function (_ super) {_ _ extends (Device, _ super); function Device () {return _ super! = = null & & _ super.apply (this, arguments) | | this } Device = _ _ decorate ([Plugin ({pluginName: 'Device', plugin:' cordova-plugin-device', pluginRef: 'device', repo:' https://github.com/apache/cordova-plugin-device', platforms: ['Android',' Browser', 'iOS',' macOS', 'Windows'] }), Injectable ()], Device) Return Device;} (IonicNativePlugin))

From the generated code, @ Plugin ({...}) and @ Injectable () will eventually be converted into normal method calls, and the result of their calls will eventually be passed as an array to the _ _ decorate function, while inside the _ _ decorate function, their respective type decorators will be called with the Device class as parameters, thus extending the corresponding functionality.

9.2 Classification of decorators

In TypeScript, decorators are divided into four categories: class decorator, attribute decorator, method decorator and parameter decorator.

9.2.1 Class decorator

Class decorator declaration:

Declare type ClassDecorator = (target: TFunction) = > TFunction | void

Class decorator, as its name implies, is used to decorate classes. It receives one parameter:

Target: TFunction-the class being decorated

After the first glance, do you feel bad? It's all right. Let's give an example right away:

Function Greeter (target: Function): void {target.prototype.greet = function (): void {console.log ("Hello Semlinker!");};} @ Greeter class Greeting {constructor () {/ / Internal implementation}} let myGreeting = new Greeting (); myGreeting.greet (); / / console output: 'Hello semlinkers'

In the above example, we defined the Greeter class decorator, and we used the @ Greeter syntax sugar to use the decorator.

Tip: readers can directly copy the above code and run it in TypeScript Playground to view the results.

9.2.2 attribute decorator

Property decorator declaration:

Declare type PropertyDecorator = (target:Object, propertyKey: string | symbol) = > void

The property decorator, as its name implies, is used to decorate the properties of a class. It receives two parameters:

Target: Object-the class being decorated

PropertyKey: string | symbol-the attribute name of the decorated class

Strike while the iron is hot, let's give an example to warm up:

Function logProperty (target: any, key: string) {delete target [key]; const backingField = "_" + key; Object.defineProperty (target, backingField, {writable: true, enumerable: true, configurable: true}); / / property getter const getter = function (this: any) {const currVal = this [backingField]; console.log (`Get: ${key} = > ${currVal} `); return currVal;} / / property setter const setter = function (this: any, newVal: any) {console.log (`Set: ${key} = > ${newVal} `); this [backingField] = newVal;}; / / Create new property with getter and setter Object.defineProperty (target, key, {get: getter, set: setter, enumerable: true, configurable: true});} class Person {@ logProperty public name: string Constructor (name: string) {this.name = name;}} const p1 = new Person ("semlinker"); p1.name = "kakuqo"

In the above code, we define a logProperty function to track the user's actions on the attribute. When the code runs successfully, the following result will be output in the console:

Set: name = > semlinker Set: name = > kakuqo

9.2.3 method decorator

Method decorator declares:

Declare type MethodDecorator = (target:Object, propertyKey: string | symbol, descriptor: TypePropertyDescript) = > TypedPropertyDescriptor | void

Method decorator, as its name implies, is used to decorate the method of a class. It receives three parameters:

Target: Object-the class being decorated

PropertyKey: string | symbol-method name

Descriptor: TypePropertyDescript-attribute descriptor

Don't talk too much nonsense, go straight to the example:

Function LogOutput (tarage: Function, key: string, descriptor: any) {let originalMethod = descriptor.value; let newMethod = function (. Args: any []): any {let result: any = originalMethod.apply (this, args); if (! this.loggedOutput) {this.loggedOutput = new Array ();} this.loggedOutput.push ({method: key, parameters: args, output: result, timestamp: new Date ()}) Return result;}; descriptor.value = newMethod;} class Calculator {@ LogOutput double (num: number): number {return num * 2;}} let calc = new Calculator (); calc.double (11); / / console ouput: [{method: "double", output: 22,...}] console.log (calc.loggedOutput)

9.2.4 Parameter decorator

Parameter decorator declaration:

Declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) = > void

As the name implies, the parameter decorator is used to decorate function parameters, and it receives three parameters:

Target: Object-the class being decorated

PropertyKey: string | symbol-method name

ParameterIndex: number-the index value of the parameter in the method

FunctionLog (target: Function, key: string, parameterIndex: number) {let functionLogged = key | | target.prototype.constructor.name; console.log (`The parameter in position ${parameterIndex} at ${functionLogged} has been decorated`);} class Greeter {greeting: string; constructor (@ Log phrase: string) {this.greeting = phrase;}} / console output: The parameter in position 0 / / at Greeter has been decorated

10. # XXX private field

ECMAScript private fields are supported in TypeScript 3.8 and are used as follows:

Class Person {# name: string; constructor (name: string) {this.#name = name;} greet () {console.log (`Hello, my name is ${this.#name}! `);} let semlinker = new Person ("Semlinker"); semlinker.#name; / ~ / / Property'# name' is not accessible outside class' Person' / / because it has a private identifier.

Unlike regular properties (even those declared with the private modifier), private fields should keep the following rules in mind:

Private fields begin with the # character, which we sometimes call private names

Each private field name is uniquely limited to the class it contains

You cannot use TypeScript accessibility modifiers (such as public or private) on private fields

Private fields cannot be accessed outside the included class or even detected.

10.1 the difference between private fields and private

Speaking of which, what's the difference between a private field defined with # and a field defined with the private modifier? Now let's look at an example of private:

Class Person {constructor (private name: string) {}} let person = new Person ("Semlinker"); console.log (person.name)

In the above code, we created a Person class that uses the private modifier to define a private property name, then uses this class to create a person object, and then accesses the private property of the person object through person.name, and the TypeScript compiler prompts the following exception:

Property 'name' is private and only accessible within class' Person'. (2341)

So how to solve this anomaly? Of course, you can use type assertions to convert person to any:

Console.log ((person as any) .name)

Although the exception hints of the TypeScript compiler are solved in this way, we still have access to private properties within the Person class at run time. Why is that? Let's take a look at the compiled ES5 code, and maybe you'll know the answer:

Var Person = / * * @ class * / (function () {function Person (name) {this.name = name;} return Person;} ()); var person = new Person ("Semlinker"); console.log (person.name)

At this time, I believe some partners will wonder what code will be generated after compiling through the private fields defined by # in TypeScript version 3.8 and above:

Class Person {# name: string; constructor (name: string) {this.#name = name;} greet () {console.log (`Hello, my name is ${this.#name}! `);}}

The above code target is set to ES2015 and the following code is compiled:

"use strict"; var _ classPrivateFieldSet = (this & & this.__classPrivateFieldSet) | | function (receiver, privateMap, value) {if (! privateMap.has (receiver)) {throw new TypeError ("attempted to set private field on non-instance");} privateMap.set (receiver, value); return value;} Var _ _ classPrivateFieldGet = (this & & this.__classPrivateFieldGet) | | function (receiver, privateMap) {if (! privateMap.has (receiver)) {throw new TypeError ("attempted to get private field on non-instance");} return privateMap.get (receiver);}; var _ name; class Person {constructor (name) {_ name.set (this, void 0); _ _ classPrivateFieldSet (this, _ name, name) } greet () {console.log (`Hello, my name is ${_ _ classPrivateFieldGet (this, _ name)}! `);}} _ name = new WeakMap ()

By observing the above code, using the ECMAScript private field defined by the # sign, it is stored through the WeakMap object, and the compiler generates _ _ classPrivateFieldSet and _ _ classPrivateFieldGet methods to set and get values.

Thank you for your reading, the above is the content of "what strange symbols are there in TypeScript". After the study of this article, I believe you have a deeper understanding of what strange symbols are in TypeScript, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report