The first time Abo used TypeScript was in an Angular 2.x project, before TypeScript was in the public domain. In this article, we will guide you through 16 ways to learn TypeScript.

What is TypeScript

TypeScript is a free and open source programming language developed by Microsoft. It is a superset of JavaScript and essentially adds optional static typing and class-based object-oriented programming to the language.

TypeScript offers the latest and evolving JavaScript features, including those from ECMAScript 2015 and future proposals, such as asynchronous functionality and Decorators, to help build robust components. The following figure shows the relationship between TypeScript and ES5, ES2015, and ES2016:

1.1 Differences between TypeScript and JavaScript

TypeScript JavaScript
Supersets of JavaScript are used to address the code complexity of large projects A scripting language used to create dynamic web pages
Errors can be found and corrected during compilation As an interpreted language, errors can only be found at runtime
Strong typing, support for static and dynamic typing Weakly typed, with no static typing option
It is eventually compiled into JavaScript code that the browser can understand It can be used directly in the browser
Supports modules, generics, and interfaces Modules, generics, or interfaces are not supported
Community support is still growing, and it’s not huge yet Lots of community support and lots of documentation and problem-solving support

1.2 get TypeScript

The command-line TypeScript compiler can be installed using the NPM package manager.

1. Install the TypeScript
$ npm install -g typescript
Copy the code
2. Verify the TypeScript
$ tsc -v 
# Version 4.0.2
Copy the code
3. Compile the TypeScript file
$ tsc helloworld.ts
# helloworld.ts => helloworld.js
Copy the code

Of course, beginners to TypeScript can use the online TypeScript Playground to learn new syntax or new features without installing TypeScript. By configuring the TS Config Target, you can set different compilation targets to compile different object code.

The compilation target set in the following example is ES5:

(photo: www.typescriptlang.org/play)

1.3 Typical TypeScript workflows

As you can see, there are three TS files in the figure above: A.ts, B.TS, and C.TS. These files are compiled by the TypeScript compiler into three JS files, a.js, B.JS, and C.JS, according to the configured compilation options. For most Web projects that use TypeScript, we also package the compiled JS files and then deploy them.

1.4 First experience with TypeScript

Create a new hello.ts file and enter the following:

function greet(person: string) {
  return 'Hello, ' + person;
}

console.log(greet("TypeScript"));
Copy the code

Then execute the TSC hello.ts command, which will generate a compiled file hello.js:

"use strict";
function greet(person) {
  return 'Hello, ' + person;
}
console.log(greet("TypeScript"));
Copy the code

Looking at the compiled output above, we can see that the type information for the Person parameter was erased after compilation. TypeScript only statically checks types at compile time. If an error is found, an error is reported at compile time. At run time, the compiled JS is just like a normal JavaScript file, with no type checking.

Base TypeScript types

2.1 a Boolean type

let isDone: boolean = false;
// ES5: var isDone = false;
Copy the code

2.2 Number type

let count: number = 10;
// ES5: var count = 10;
Copy the code

2.3 type String

let name: string = "semliker";
// ES5: var name = 'semlinker';
Copy the code

2.4 Symbol type

const sym = Symbol(a);let obj = {
  [sym]: "semlinker"};console.log(obj[sym]); // semlinker 
Copy the code

2.5 Array type

let list: number[] = [1.2.3];
// ES5: var list = [1,2,3];

let list: Array<number> = [1.2.3]; // Array
      
        generic syntax
      
// ES5: var list = [1,2,3];
Copy the code

2.6 Enum type

Using enumerations we can define constants with names. Use enumerations to articulate intent or to create a distinct set of use cases. TypeScript supports numeric and string-based enumerations.

1. Enumeration of numbers
enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dir: Direction = Direction.NORTH;
Copy the code

By default, NORTH starts at 0, and the remaining members automatically grow from 1. In other words, the value of direction.south is 1, the value of direction.east is 2, and the value of direction.west is 3.

The above enumeration example, when compiled, corresponds to the following ES5 code:

"use strict";
var Direction;
(function (Direction) {
  Direction[(Direction["NORTH"] = 0)] = "NORTH";
  Direction[(Direction["SOUTH"] = 1)] = "SOUTH";
  Direction[(Direction["EAST"] = 2)] = "EAST";
  Direction[(Direction["WEST"] = 3)] = "WEST";
})(Direction || (Direction = {}));
var dir = Direction.NORTH;
Copy the code

We can also set the initial value of NORTH, such as:

enum Direction {
  NORTH = 3,
  SOUTH,
  EAST,
  WEST,
}
Copy the code
2. String enumeration

In TypeScript 2.4, we are allowed to use string enumerations. In a string enumeration, each member must be initialized with a string literal, or with another string enumeration member.

enum Direction {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST",}Copy the code

The above code corresponds to the following ES5 code:

"use strict";
var Direction;
(function (Direction) {
    Direction["NORTH"] = "NORTH";
    Direction["SOUTH"] = "SOUTH";
    Direction["EAST"] = "EAST";
    Direction["WEST"] = "WEST";
})(Direction || (Direction = {}));
Copy the code

By observing the compilation of numeric and string enumerations, we can see that numeric enumerations support a reverse mapping from member values to member names in addition to the normal mapping from member names to member values:

enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dirName = Direction[0]; // NORTH
let dirVal = Direction["NORTH"]; / / 0
Copy the code

Also, for pure string enumerations, we cannot omit any initializer. Numerical enumerations that have no explicit value are initialized using the default rules.

3. Constant enumeration

In addition to numeric and string enumerations, there is a special kind of enumeration called constant enumerations. It is an enumeration decorated with a const keyword. Constant enumerations use inline syntax and do not generate any JavaScript for the enumeration type compilation. To better understand this sentence, let’s look at a specific example:

const enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dir: Direction = Direction.NORTH;
Copy the code

The above code corresponds to the following ES5 code:

"use strict";
var dir = 0 /* NORTH */;
Copy the code
4. Heterogeneous enumeration

The member values of a heterogeneous enumeration are a mixture of numbers and strings:

enum Enum {
  A,
  B,
  C = "C",
  D = "D",
  E = 8,
  F,
}
Copy the code

The above code for ES5 is as follows:

"use strict";
var Enum;
(function (Enum) {
    Enum[Enum["A"] = 0] = "A";
    Enum[Enum["B"] = 1] = "B";
    Enum["C"] = "C";
    Enum["D"] = "D";
    Enum[Enum["E"] = 8] = "E";
    Enum[Enum["F"] = 9] = "F";
})(Enum || (Enum = {}));
Copy the code

By looking at the generated ES5 code above, we can see that numeric enumerations are more “reverse mapped” than string enumerations:

console.log(Enum.A) // Output: 0
console.log(Enum[0]) // Output: A
Copy the code

2.7 Any type

In TypeScript, any type can be classified as any. This makes the any type the top-level type of the type system (also known as the global supertype).

let notSure: any = Awesome!;
notSure = "semlinker";
notSure = false;
Copy the code

The any type is essentially an escape pod for the type system. As developers, this gives us a lot of freedom: TypeScript allows us to do anything with a value of type any without performing any kind of checks beforehand. Such as:

let value: any;

value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0] [1]; // OK
Copy the code

In many scenarios, this is too loose. Using the any type, you can easily write code that is of the right type but problematic at run time. If we use any, we won’t be able to use many of TypeScript’s protections. To address the problem of any, TypeScript 3.0 introduces unknown types.

2.8 Unknown type

Just as all types can be assigned to any, all types can be assigned to unknown. This makes Unknown another top-level type in the TypeScript type system (the other is any). Here’s an example of how unknown can be used:

let value: unknown;

value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(a);// OK
value = Symbol("type"); // OK
Copy the code

All assignments to the value variable are considered type-correct. But what happens when we try to assign a value of type unknown to a variable of another type?

let value: unknown;

let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
Copy the code

The unknown type can only be assigned to the any type and the unknown type itself. Intuitively, this makes sense: Only containers that can hold values of any type can hold values of type unknown. After all, we don’t know what type of value is stored in the variable value.

Now let’s see what happens when we try to operate on a value of type unknown. Here is the same action we looked at in the previous Any chapter:

let value: unknown;

value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0] [1]; // Error
Copy the code

With the value variable type set to unknown, these operations are no longer considered type-correct. By changing the any type to the unknown type, we have changed the default setting, which allows all changes, to disallow any changes.

2.9 the Tuple type

As we all know, arrays usually consist of values of the same type, but sometimes we need to store different types of values in a single variable, and we can use tuples. There are no tuples in JavaScript. Tuples are TypeScript specific types that work like arrays.

Tuples can be used to define types that have a limited number of unnamed attributes. Each attribute has an associated type. When using tuples, you must supply the value of each attribute. To make the concept of tuples more intuitive, let’s look at a concrete example:

let tupleType: [string.boolean];
tupleType = ["semlinker".true];
Copy the code

In the above code, we define a variable named tupleType, whose type is an array of types [string, Boolean], and then we initialize the tupleType variables in order of the correct type. As with arrays, we can access the elements of a tuple by subscript:

console.log(tupleType[0]); // semlinker
console.log(tupleType[1]); // true
Copy the code

If a type mismatch occurs during tuple initialization, for example:

tupleType = [true."semlinker"];
Copy the code

At this point, the TypeScript compiler displays the following error message:

[0]: Type 'true' is not assignable to type 'string'.
[1]: Type 'string' is not assignable to type 'boolean'.
Copy the code

It’s obviously a type mismatch. During tuple initialization, we must also provide the value of each attribute, otherwise we will also have an error, such as:

tupleType = ["semlinker"];
Copy the code

At this point, the TypeScript compiler displays the following error message:

Property '1' is missing in type '[string]' but required in type '[string, boolean]'.
Copy the code

2.10 Void type

In some ways, the void type looks like the opposite of any. It means there is no type. When a function does not return a value, you will usually see a return value of type void:

// Declare the function return void
function warnUser() :void {
  console.log("This is my warning message");
}
Copy the code

The ES5 code generated by compiling the above code is as follows:

"use strict";
function warnUser() {
  console.log("This is my warning message");
}
Copy the code

Note that declaring a void variable is useless, since in strict mode it can only be undefined:

let unusable: void = undefined;
Copy the code

2.11 Null and Undefined types

In TypeScript, undefined and NULL have their own types: undefined and NULL.

let u: undefined = undefined;
let n: null = null;
Copy the code

2.12 Object, object and {} types

1. The object type

The object type is a new type introduced in TypeScript 2.2 and is used to represent non-primitive types.

// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
  create(o: object | null) :any;
  // ...
}

const proto = {};

Object.create(proto);     // OK
Object.create(null);      // OK
Object.create(undefined); // Error
Object.create(1337);      // Error
Object.create(true);      // Error
Object.create("oops");    // Error
Copy the code
2. The Object type

Type Object: This is the type of all instances of class Object. It is defined by the following two interfaces:

  • The Object interface defines properties on the object. prototype Object.
// node_modules/typescript/lib/lib.es5.d.ts
interface Object {
  constructor: Function;
  toString(): string;
  toLocaleString(): string;
  valueOf(): Object;
  hasOwnProperty(v: PropertyKey): boolean;
  isPrototypeOf(v: Object) :boolean;
  propertyIsEnumerable(v: PropertyKey): boolean;
}
Copy the code
  • The ObjectConstructor interface defines the properties of the Object class.
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
  /** Invocation via `new` */
  new(value? :any) :Object;
  /** Invocation via function calls */(value? :any) :any;
  readonly prototype: Object;
  getPrototypeOf(o: any) :any;
  / /...
}

declare var Object: ObjectConstructor;
Copy the code

All instances of class Object inherit all properties from the Object interface.

3. {} type

The {} type describes an object that has no members. TypeScript produces a compile-time error when you try to access any property of such an object.

// Type {}
const obj = {};

// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
Copy the code

However, you can still use all the properties and methods defined on the Object type, which are implicitly available through JavaScript’s prototype chain:

// Type {}
const obj = {};

// "[object Object]"
obj.toString();
Copy the code

2.13 Never type

The never type represents the type of values that will never exist. For example, the type never is the return value type for function or arrow function expressions that always throw an exception or never return a value at all.

// A function that returns never must have an unreachable endpoint
function error(message: string) :never {
  throw new Error(message);
}

function infiniteLoop() :never {
  while (true) {}}Copy the code

In TypeScript, you can use the never feature to implement comprehensiveness checks. Here’s an example:

type Foo = string | number;

function controlFlowAnalysisWithNever(foo: Foo) {
  if (typeof foo === "string") {
    // Here foo is narrowed to string
  } else if (typeof foo === "number") {
    // Here foo is narrowed to the number type
  } else {
    // Foo in this case is never
    const check: never= foo; }}Copy the code

Notice that in the else branch, we assign foo, narrowed down to never, to a variable that displays the declaration of never. If all logic is correct, then this should compile through. But suppose one day your colleague changes the type of Foo:

type Foo = string | number | boolean;
Copy the code

But he forgot to modify controlFlowAnalysisWithNever method in control process at the same time, this time the else branch foo type can be narrowed to a Boolean type, lead to cannot be assigned to never type, then will generate a compiler error. In this way, we can make sure that

ControlFlowAnalysisWithNever method always end with all possible types of Foo. From this example, we can conclude that using never avoids the possibility that there is no corresponding implementation for a newly joined type, in order to write type-safe code.

TypeScript assertions

3.1 Type Assertion

Sometimes you will encounter situations where you know more about a value than TypeScript does. Usually this happens when you clearly know that an entity has a more precise type than it already has.

Type assertions are a way of telling the compiler, “Trust me, I know what I’m doing.” Type assertions are like conversions in other languages, but without the special data checking and deconstruction. It has no runtime impact, only at compile time.

Type assertions come in two forms:

1. Angle bracket syntax
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
Copy the code
2. As the grammar
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
Copy the code

3.2 Non-empty assertions

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

So what is the use of a non-empty assertion operator? Let’s first look at some usage scenarios for the non-empty assertion operator.

1. Ignore the 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= maybeString! ;// Ok
}
Copy the code
2. Ignore the undefined type when calling the 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
  constnum2 = numGenerator! (a);//OK
}
Copy the code

Because! Non-empty assertion operators are removed from compiled JavaScript code, so be careful when using them. Here’s an example:

const a: number | undefined = undefined;
const b: number= a! ;console.log(b); 
Copy the code

The above TS code will compile to produce the following ES5 code:

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

Although in TS code, we use a non-empty assertion such that const b: number = a! ; Statements are checked by the TypeScript type checker. But in the generated ES5 code,! The non-empty assertion operator has been removed, so executing the above code in the browser will print undefined in the console.

3.3 Determine assignment assertions

TypeScript version 2.7 introduced deterministic assignment assertions that allow instance property and variable declarations to be followed by one! To tell TypeScript that the property will be explicitly assigned. To better understand what it does, let’s look at a concrete example:

let x: number;
initialize();
// Variable 'x' is used before being assigned.(2454)
console.log(2 * x); // Error

function initialize() {
  x = 10;
}
Copy the code

This exception message clearly means that the variable x was used before assignment. To solve this problem, we can use the determine-assignment assertion:

letx! :number;
initialize();
console.log(2 * x); // Ok

function initialize() {
  x = 10;
}
Copy the code

Through the let x! : number; By determining the assignment assertion, the TypeScript compiler knows that the property will be explicitly assigned.

Type guard

A type guard is an expression that can be checked at runtime to ensure that the type is within a certain range. In other words, type protection guarantees that a string is a string, even though its value can also be a numeric value. Type protection is not entirely different from feature detection; the main idea is to try to detect attributes, methods, or stereotypes to determine how to handle values. At present, there are four main ways to realize type protection:

4.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); }}Copy the code

4.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}'. `);
}
Copy the code

Only two types of typeof protection are supported: Typeof V === “typename” and Typeof V! == TYPename, “typename” must be “number”, “string”, “Boolean” or “symbol”. But TypeScript doesn’t prevent you from comparing other strings; the language doesn’t recognize those expressions as type-protected.

4.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) {
  // The padder type is narrowed to 'SpaceRepeatingPadder'
}
Copy the code

4.4 User-defined type predicates for type protection

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

function isString(x: any) :x is string {
  return typeof x === "string";
}
Copy the code

Join types and type aliases

5.1 Union Type

Union types are usually used with null or undefined:

const sayHello = (name: string | undefined) = > {
  / *... * /
};
Copy the code

Here, for example, the type of the name is the string | undefined means that can be a string or undefined values passed to the sayHello function.

sayHello("semlinker");
sayHello(undefined);
Copy the code

From this example, you can intuitively know that A combined type of A and B is A type that accepts both A and B values. In addition, for associative types, you might encounter the following usage:

let num: 1 | 2 = 1;
type EventNames = 'click' | 'scroll' | 'mousemove';
Copy the code

The 1, 2, or ‘click’ in the above examples are called literal types and are used to restrict the value to one of several values.

5.2 Discernable association

TypeScript Discriminated Unions types are also known as algebraic data types or label union types. It has three main points: identifiers, federated types, and type guards.

The essence of this type is a type protection method that combines union types and literal types. If a type is a union type of multiple types, and the types have a common property, you can use this common property to create different types of protected blocks.

1. Can recognize

Identifiers require that each element in the union type contains a singleton type attribute, such as:

enum CarTransmission {
  Automatic = 200,
  Manual = 300
}

interface Motorcycle {
  vType: "motorcycle"; // discriminant
  make: number; // year
}

interface Car {
  vType: "car"; // discriminant
  transmission: CarTransmission
}

interface Truck {
  vType: "truck"; // discriminant
  capacity: number; // in tons
}
Copy the code

In the above code, we define three interfaces, namely Motorcycle, Car and Truck, and each of these interfaces contains a vType attribute, which is called the identifiable attribute, while other attributes are only related to the characteristic interface.

2. Union type

Based on the three interfaces defined previously, we can create a Vehicle union type:

type Vehicle = Motorcycle | Car | Truck;
Copy the code

Now we can start using the Vehicle union type, which can represent different types of vehicles for a variable of Vehicle type.

3. Type guard

Let’s define an evaluatePrice method to calculate the price based on the vehicle type, capacity, and evaluation factor, as follows:

const EVALUATION_FACTOR = Math.PI; 

function evaluatePrice(vehicle: Vehicle) {
  return vehicle.capacity * EVALUATION_FACTOR;
}

const myTruck: Truck = { vType: "truck".capacity: 9.5 };
evaluatePrice(myTruck);
Copy the code

For the above code, the TypeScript compiler will prompt the following error:

Property 'capacity' does not exist on type 'Vehicle'.
Property 'capacity' does not exist on type 'Motorcycle'.
Copy the code

The reason is that there is no capacity attribute for Motorcycle, and there is no capacity attribute for Car. So how can we solve these problems now? At this point, we can use the type guard. Let’s refactor the evaluatePrice method to look like this:

function evaluatePrice(vehicle: Vehicle) {
  switch(vehicle.vType) {
    case "car":
      return vehicle.transmission * EVALUATION_FACTOR;
    case "truck":
      return vehicle.capacity * EVALUATION_FACTOR;
    case "motorcycle":
      returnvehicle.make * EVALUATION_FACTOR; }}Copy the code

In the above code, we implement the type guard using the switch and case operators to ensure that in the evaluatePrice method, we can safely access the properties contained in the Vehicle object to correctly calculate the price for the vehicle type.

5.3 Type Alias

A type alias is used to give a type a new name.

type Message = string | string[];

let greet = (message: Message) = > {
  // ...
};
Copy the code

Cross type

Crosstyping in TypeScript is the merging of multiple types into a single type. With the & operator, existing types can be added together to form a type that contains all the required features of the type.

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

let point: Point = {
  x: 1.y: 1
}
Copy the code

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

6.1 Merging attributes of the base type with the same name

Let’s say 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. For example:

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;
Copy the code

In the above code, interface X and interface Y both contain the same member C, but they are of different types. In this case, can the member C of type XY or type YX be of type string or number? Take the following example:

p = { c: 6.d: "d".e: "e" }; 
Copy the code

q = { c: "c".d: "d".e: "e" }; 
Copy the code

Why is member C of type Never when interface X and interface Y are mixed in? This is because member C is of type string & number, meaning that the type of member C can be either string or number. Obviously this type does not exist, so the member C is of type never.

6.2 Merging attributes of non-underlying types with the same name

In the above example, it happens that the type of the internal member C in both interface X and interface Y is a basic data type, so what happens if it is not a basic data type? 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: Awesome!}};console.log('abc:', abc);
Copy the code

After the above code runs successfully, the console will output the following:

It can be seen from the figure above that when multiple types are mixed, if the same member exists and the member type is not the basic data type, the merge can be successful.

TypeScript functions

7.1 Differences between TypeScript functions and JavaScript functions

TypeScript JavaScript
Contains the type No type
Arrow function Arrow function (ES2015)
Function types No function type
This parameter is mandatory and optional All parameters are optional
The default parameters The default parameters
The remaining parameters The remaining parameters
Function overloading No function overload

7.2 Arrow Function

1. Common grammar
myBooks.forEach(() = > console.log('reading'));

myBooks.forEach(title= > console.log(title));

myBooks.forEach((title, idx, arr) = >
  console.log(idx + The '-'+ title); ) ; myBooks.forEach((title, idx, arr) = > {
  console.log(idx + The '-' + title);
});
Copy the code
2. Example
// The arrow function is not used
function Book() {
  let self = this;
  self.publishDate = 2016;
  setInterval(function () {
    console.log(self.publishDate);
  }, 1000);
}

// Use the arrow function
function Book() {
  this.publishDate = 2016;
  setInterval(() = > {
    console.log(this.publishDate);
  }, 1000);
}
Copy the code

7.3 Parameter Types and Return Types

function createUserId(name: string, id: number) :string {
  return name + id;
}
Copy the code

7.4 Function Types

let IdGenerator: (chars: string, nums: number) = > string;

function createUserId(name: string, id: number) :string {
  return name + id;
}

IdGenerator = createUserId;
Copy the code

7.5 This parameter is optional

// This parameter is optional
function createUserId(name: string, id: number, age? :number) :string {
  return name + id;
}

// Default parameters
function createUserId(
  name: string = "semlinker",
  id: number, age? :number
) :string {
  return name + id;
}
Copy the code

When you declare a function, you can pass? Number to define optional parameters, such as age? : number In practice, it is important to place the optional arguments after the normal arguments, otherwise it will cause a compilation error.

7.6 Remaining Parameters

function push(array, ... items) {
  items.forEach(function (item) {
    array.push(item);
  });
}

let a = [];
push(a, 1.2.3);
Copy the code

7.7 Function Overloading

Function overload or method overload is the ability to create multiple methods with the same name and different numbers or types of arguments.

function add(a: number, b: number) :number;
function add(a: string, b: string) :string;
function add(a: string, b: number) :string;
function add(a: number, b: string) :string;
function add(a: Combinable, b: Combinable) {
  // type Combinable = string | number;
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}
Copy the code

In the code above, we overload the add function by providing multiple function type definitions. In TypeScript, in addition to overloading normal functions, we can also override member methods in classes.

Method overloading is a technique in which a method with the same name in the same class has different parameters (different parameter types, different number of parameters, or different order of parameters for the same number of parameters), and the method that matches the argument is selected to perform the operation based on the form of the argument. So a member method in a class can be overloaded only if it has the same name and different argument lists in the same class. Here’s an example of member method overloading:

class Calculator {
  add(a: number.b: number) :number;
  add(a: string.b: string) :string;
  add(a: string.b: number) :string;
  add(a: number.b: string) :string;
  add(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
    returna + b; }}const calculator = new Calculator();
const result = calculator.add('Semlinker'.' Kakuqo');
Copy the code

The important thing to note here is that when the TypeScript compiler handles function overloading, it looks up the list of overloads and tries to use the first overload definition. Use this if there is a match. Therefore, when defining overloads, be sure to put the most precise definition first. Also in the Calculator class, add(A: Combinable, B: Combinable){} is not part of the overload list, so we only define four overload methods for the Add member method.

TypeScript arrays

8.1 Array Deconstruction

let x: number; let y: number; let z: number;
let five_array = [0.1.2.3.4];
[x,y,z] = five_array;
Copy the code

8.2 Array expansion operator

let two_array = [0.1];
let five_array = [...two_array, 2.3.4];
Copy the code

8.3 Array Traversal

let colors: string[] = ["red"."green"."blue"];
for (let i of colors) {
  console.log(i);
}
Copy the code

9. TypeScript objects

9.1 Object Deconstruction

let person = {
  name: "Semlinker".gender: "Male"};let { name, gender } = person;
Copy the code

9.2 Object expansion operator

let person = {
  name: "Semlinker".gender: "Male".address: "Xiamen"};// Assemble the object
letpersonWithAge = { ... person,age: 33 };

// Get all but certain items
let{ name, ... rest } = person;Copy the code

TypeScript interfaces

In object-oriented language, interface is a very important concept, it is the abstraction of behavior, and the concrete action needs to be implemented by the class.

Interfaces in TypeScript are a very flexible concept. In addition to abstracting part of the behavior of a class, they are often used to describe the Shape of an object.

10.1 Shapes of objects

interface Person {
  name: string;
  age: number;
}

let semlinker: Person = {
  name: "semlinker".age: 33};Copy the code

10.2 optional | read-only property

interface Person {
  readonly name: string; age? :number;
}
Copy the code

The read-only property is used to restrict the value of an object to be changed only when it is newly created. TypeScript also provides ReadonlyArray

, which is similar to Array

except that all mutable methods are removed, thus ensuring that the Array cannot be modified once it has been created.

let a: number[] = [1.2.3.4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
Copy the code

10.3 Arbitrary Attributes

Sometimes we want an interface to have arbitrary attributes in addition to mandatory and optional attributes, and we can use the form of index signatures to meet this requirement.

interface Person {
  name: string; age? :number;
  [propName: string] :any;
}

const p1 = { name: "semlinker" };
const p2 = { name: "lolo".age: 5 };
const p3 = { name: "kakuqo".sex: 1 }
Copy the code

10.4 Differences between Interfaces and Type Aliases

1.Objects/Functions

Both interface and type aliases can be used to describe the shape or function signature of an object:

interface

interface Point {
  x: number;
  y: number;
}

interface SetPoint {
  (x: number.y: number) :void;
}
Copy the code

Type the alias

type Point = {
  x: number;
  y: number;
};

type SetPoint = (x: number, y: number) = > void;
Copy the code
2.Other Types

Unlike interface types, type aliases can be used for some other types, such as primitive types, federated types, and tuples:

// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number.string];
Copy the code
3.Extend

Both interface and type aliases can be extended, but the syntax is different. In addition, interfaces and type aliases are not mutually exclusive. Interfaces can extend type aliases, but not vice versa.

Interface extends interface

interface PartialPointX { x: number; }
interface Point extends PartialPointX { 
  y: number; 
}
Copy the code

Type alias extends type alias

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };
Copy the code

Interface extends type alias

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }
Copy the code

Type alias extends interface

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };
Copy the code
4.Implements

A class can implement an interface or type alias in the same way, but a class cannot implement a federated type defined using a type alias:

interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x = 1;
  y = 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x = 1;
  y = 2;
}

type PartialPoint = { x: number; } | { y: number; };

// A class can only implement an object type or 
// intersection of object types with statically known members.
class SomePartialPoint implements PartialPoint { // Error
  x = 1;
  y = 2;
}
Copy the code
5.Declaration merging

Unlike type aliases, interfaces can be defined multiple times and are automatically merged into a single interface.

interface Point { x: number; }
interface Point { y: number; }

const point: Point = { x: 1.y: 2 };
Copy the code

TypeScript classes

11.1 Properties and methods of the class

In an object-oriented language, a class is a construct of an object-oriented computer programming language. It is a blueprint for creating an object, describing the common properties and methods of the created object.

In TypeScript, we can define a Class using the Class keyword:

class Greeter {
  // Static properties
  static cname: string = "Greeter";
  // Member attributes
  greeting: string;

  // Constructor - performs the initialization operation
  constructor(message: string) {
    this.greeting = message;
  }

  // Static methods
  static getClassName() {
    return "Class name is Greeter";
  }

  // Member methods
  greet() {
    return "Hello, " + this.greeting; }}let greeter = new Greeter("world");
Copy the code

So what’s the difference between a member property and a static property, or between a member method and a static method? Without further explanation, let’s take a look at the compiled ES5 code:

"use strict";
var Greeter = / * *@class * / (function () {
    // Constructor - performs the initialization operation
    function Greeter(message) {
      this.greeting = message;
    }
    // Static methods
    Greeter.getClassName = function () {
      return "Class name is Greeter";
    };
    // Member methods
    Greeter.prototype.greet = function () {
      return "Hello, " + this.greeting;
    };
    // Static properties
    Greeter.cname = "Greeter";
    returnGreeter; } ());var greeter = new Greeter("world");
Copy the code

11.2 ECMAScript Private Fields

ECMAScript private fields have been supported since TypeScript version 3.8. They can be used as follows:

class Person {
  #name: string;

  constructor(name: string) {
    this.#name = name;
  }

  greet() {
    console.log(`Hello, my name is The ${this.#name}! `); }}let semlinker = new Person("Semlinker");

semlinker.#name;
/ / ~ ~ ~ ~ ~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.
Copy the code

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

  • Private fields to#Character, sometimes we call it a private name;
  • Each private field name is uniquely qualified 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 of the contained class and cannot even be detected.

11.3 the visitor

In TypeScript, getters and setters are used to encapsulate and validate data 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";
if (employee.fullName) {
  console.log(employee.fullName);
}
Copy the code

11.4 Class inheritance

Inheritance is a hierarchical model that connects classes to classes. The ability for a class (called a subclass, or subinterface) to inherit the functionality of another class (called a parent class, or parent interface) and add new functionality of its own. Inheritance is the most common relationship between classes or interfaces.

Inheritance is an IS-A relationship:

In TypeScript, we can implement inheritance with the extends keyword:

class Animal {
  name: string;
  
  constructor(theName: string) {
    this.name = theName;
  }
  
  move(distanceInMeters: number = 0) {
    console.log(`The ${this.name} moved ${distanceInMeters}m.`); }}class Snake extends Animal {
  constructor(name: string) {
    super(name); // Call the constructor of the superclass
  }
  
  move(distanceInMeters = 5) {
    console.log("Slithering...");
    super.move(distanceInMeters); }}let sam = new Snake("Sammy the Python");
sam.move();
Copy the code

11.5 the abstract class

A class declared using the abstract keyword is called an abstract class. An abstract class cannot be instantiated because it contains one or more abstract methods. An abstract method is one that contains no concrete 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
Copy the code

Abstract classes cannot be instantiated directly; we can only instantiate subclasses that implement all abstract methods. The details are as follows:

abstract class Person {
  constructor(public name: string){}

  // Abstract method
  abstract say(words: string) :void;
}

class Developer extends Person {
  constructor(name: string) {
    super(name);
  }
  
  say(words: string) :void {
    console.log(`The ${this.name} says ${words}`); }}const lolo = new Developer("lolo");
lolo.say("I love ts!"); // lolo says I love ts!
Copy the code

11.6 Class method overloading

In the previous section, we covered function overloading. For methods of a class, it also supports overloading. For example, in the following example we override the getProducts member method of the ProductService class:

class ProductService {
    getProducts(): void;
    getProducts(id: number) :void;
    getProducts(id? :number) {
      if(typeof id === 'number') {
          console.log('Obtain id as${id}Product information ');
      } else {
          console.log('Get all product information'); }}}const productService = new ProductService();
productService.getProducts(Awesome!); // Obtain the product information with id 666
productService.getProducts(); // Obtain all product information
Copy the code

TypeScript generics

In software engineering, we should not only create consistent, well-defined apis, but also consider reusability. Components can support not only current data types, but future data types as well, which gives you great flexibility when creating large systems.

In languages like C# and Java, you can use generics to create reusable components that can support multiple types of data. This allows users to use components with their own data types.

The key purpose of designing generics is to provide meaningful constraints between members: instance members of the class, methods of the class, function arguments, and function return values.

Generics is a template that allows the same function to accept parameters of different types. Instead of using the any type, it is better to use generics to create reusable components because generics preserve the parameter types.

12.1 Generic syntax

For readers new to TypeScript generics, the

syntax will be unfamiliar for the first time. It’s nothing special. Just like passing arguments, we’re passing the type that we want to use for a particular function call.

Referring to the above image, when we call identity

(1), the Number type is just like the argument 1, and it will populate the type wherever T appears. The T inside the

diagram is called a type variable. It is the type placeholder we want to pass to the identity function, and it is assigned to the value argument in place of its type: T acts as the type here, not the specific type Number.

Where T stands for Type and is often used as the first Type variable name when defining generics. But actually T can be replaced by any valid name. In addition to T, here are the meanings of common generic variables:

  • K (Key) : indicates the Key type in the object;
  • V (Value) : indicates the Value type of an object.
  • E (Element) : indicates the Element type.

Instead of defining just one type variable, we can introduce as many type variables as we want. For example, we introduce a new type variable U that extends our defined identity function:

function identity <T.U> (value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity<Number.string> (68."Semlinker"));
Copy the code

In addition to explicitly setting values for type variables, a more common approach is to have the compiler automatically select these types, thus making the code more concise. We can leave out the Angle brackets entirely, as in:

function identity <T.U> (value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity(68."Semlinker"));
Copy the code

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 explicitly specify them.

12.2 Generic interfaces

interface GenericIdentityFn<T> {
  (arg: T): T;
}
Copy the code

12.3 a generic class

class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) = > T;
}

let myGenericNumber = new GenericNumber<number> (); myGenericNumber.zeroValue =0;
myGenericNumber.add = function (x, y) {
  return x + y;
};
Copy the code

12.4 Generic tool types

Common utility types such as Partial, Required, Readonly, Record, and ReturnType are built into TypeScript for developers’ convenience. For the sake of space, we’ll only briefly cover the Partial tool types here. But before we get into that, let’s cover some basics so you can learn about the other types of tools on your own.

1.typeof

In TypeScript, the typeof operator can be used to get the typeof a variable declaration or object.

interface Person {
  name: string;
  age: number;
}

const sem: Person = { name: 'semlinker'.age: 33 };
type Sem= typeof sem; // -> Person

function toArray(x: number) :Array<number> {
  return [x];
}

type Func = typeof toArray; // -> (x: number) => number[]
Copy the code
2.keyof

The keyof operator, introduced in TypeScript 2.1, can be used to get all keys of a type whose return type is the union type.

interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join" 
type K3 = keyof { [x: string]: Person };  // string | number
Copy the code

There are two types of index signatures supported in TypeScript, numeric and string indexes:

interface StringArray {
  / / string index - > keyof StringArray = > string | number
  [index: string] :string; 
}

interface StringArray1 {
  // Numeric index -> keyof StringArray1 => number
  [index: number] :string;
}
Copy the code

In order to support both index types, the return value of the numeric index must be a subclass of the return value of the string index. The reason for this is that when using numeric indexes, JavaScript first converts the numeric index to a string index when performing the index operation. So keyof {[x: string] : Person} returns the result of the string | number.

3.in

In is used to iterate over enumeration types:

type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
Copy the code
4.infer

In conditional type statements, you can use infer to declare a type variable and use it.

type ReturnType<T> = T extends (
  ...args: any[]
) => infer R ? R : any;
Copy the code

In the preceding code, infer R means to declare a variable to carry the type of the return value passed into the function signature. In other words, infer can be used to obtain the type of the return value of the function for later use.

5.extends

Sometimes when we define a generic that we don’t want to be too flexible or want to inherit from some class, we can add a generic constraint with the extends keyword.

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise> (arg: T) :T {
  console.log(arg.length);
  return arg;
}
Copy the code

Now the generic function is defined with constraints, so it no longer applies to any type:

loggingIdentity(3);  // Error, number doesn't have a .length property
Copy the code

In this case, we need to pass in values that conform to the constraint type, which must contain the required attributes:

loggingIdentity({length: 10.value: 3});
Copy the code
6.Partial

Partial

makes all properties of a certain type optional, right? .

Definition:

/** * node_modules/typescript/lib/lib.es5.d.ts * Make all properties in T optional */
type Partial<T> = {
  [P inkeyof T]? : T[P]; };Copy the code

In the above code, we first get all the attribute names of T through keyof T, then use in to iterate, assign the value to P, and finally get the corresponding attribute value through T[P]. In the middle? The number used to make all properties optional.

Example:

interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return{... todo, ... fieldsToUpdate }; }const todo1 = {
  title: "Learn TS".description: "Learn TypeScript"};const todo2 = updateTodo(todo1, {
  description: "Learn TypeScript Enum"});Copy the code

In the updateTodo method above, we use the Partial

tool type and define the fieldsToUpdate to be of type Partial

, that is:

{ title? :string | undefined; description? :string | undefined;
}
Copy the code

TypeScript decorators

13.1 What is a Decorator

  • It’s an expression
  • When the expression is executed, it returns a function
  • The input parameters of the function are target, name, and descriptor
  • This function may return an object that is used to configure the Target object

13.2 Classification of decorators

  • Class decorators
  • Property decorators
  • Method decorators
  • Parameter decorators

Note that to enable the experimentalDecorators feature, you must enable the experimentalDecorators compiler option on the command line or in tsconfig.json:

The command line:

tsc --target ES5 --experimentalDecorators
Copy the code

Tsconfig. Json:

{
  "compilerOptions": {
     "target": "ES5"."experimentalDecorators": true}}Copy the code

Class 13.3 decorators

Class decorators declare:

declare type ClassDecorator = <TFunction extends Function>(
  target: TFunction
) => TFunction | void;
Copy the code

Class decorators, as the name suggests, decorate classes. It takes a parameter:

  • Target: TFunction – The class to be decorated

After the first look, you don’t even feel good. Ok, let’s do an example:

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 as any).greet(); // console output: 'Hello Semlinker! ';
Copy the code

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

As a bonus, you can simply copy the above code and run it in your TypeScript Playground to see the results.

As some readers may be wondering, the example always prints Hello Semlinker! , can you customize the output greeting? That’s a good question, and the answer is yes.

The specific implementation is as follows:

function Greeter(greeting: string) {
  return function (target: Function) {
    target.prototype.greet = function () :void {
      console.log(greeting);
    };
  };
}

@Greeter("Hello TS!")
class Greeting {
  constructor() {
    // Internal implementation}}let myGreeting = new Greeting();
(myGreeting as any).greet(); // console output: 'Hello TS! ';
Copy the code

13.4 Property decorators

Property decorator declaration:

declare type PropertyDecorator = (target:Object, 
  propertyKey: string | symbol ) = > void;
Copy the code

Property decorators, as the name suggests, decorate the properties of a class. It takes two arguments:

  • Target: Object – The class to be decorated
  • PropertyKey: string | symbol – be decorating class attribute names

Strike while the iron is hot. Here’s 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";
Copy the code

We define a logProperty function to track the user’s actions on the property. When the code runs successfully, the console will print the following:

Set: name => semlinker
Set: name => kakuqo
Copy the code

13.5 Method decorators

Method decorator declaration:

declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol, 	 	
  descriptor: TypePropertyDescript<T>) = > TypedPropertyDescriptor<T> | void;
Copy the code

Method decorators, as the name suggests, decorate the methods of a class. It takes three arguments:

  • Target: Object – The class to be decorated
  • PropertyKey: string | symbol – the method name
  • Descriptor: TypePropertyDescript – Property descriptor

Without further ado, let’s go to the following example:

function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
  let originalMethod = descriptor.value;
  descriptor.value = function (. args:any[]) {
    console.log("wrapped function: before invoking " + propertyKey);
    let result = originalMethod.apply(this, args);
    console.log("wrapped function: after invoking " + propertyKey);
    return result;
  };
}

class Task {
  @log
  runTask(arg: any) :any {
    console.log("runTask invoked, args: " + arg);
    return "finished"; }}let task = new Task();
let result = task.runTask("learn ts");
console.log("result: " + result);
Copy the code

After the above code runs successfully, the console will output the following:

"wrapped function: before invoking runTask" 
"runTask invoked, args: learn ts" 
"wrapped function: after invoking runTask" 
"result: finished" 
Copy the code

Let’s introduce the parameter decorator.

13.6 Parameter decorators

Parameter decorator declaration:

declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, 
  parameterIndex: number ) = > void
Copy the code

The parameter decorator, as its name suggests, decorates function arguments. It takes three arguments:

  • Target: Object – The class to be decorated
  • PropertyKey: string | symbol – the method name
  • ParameterIndex: number – Specifies the index value of a parameter in a method
function Log(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; }}Copy the code

After the above code runs successfully, the console will output the following:

"The parameter in position 0 at Greeter has been decorated" 
Copy the code

New TypeScript 4.0 features

TypeScript 4.0 brings a number of new features. Here are just two of them.

14.1 Class attribute inference for constructors

When the noImplicitAny configuration property is enabled, TypeScript 4.0 uses control flow analysis to determine the type of property in a class:

class Person {
  fullName; // (property) Person.fullName: string
  firstName; // (property) Person.firstName: string
  lastName; // (property) Person.lastName: string

  constructor(fullName: string) {
    this.fullName = fullName;
    this.firstName = fullName.split("") [0];
    this.lastName =   fullName.split("") [1]; }}Copy the code

However, if the code is earlier than TypeScript 4.0, such as version 3.9.2, the compiler will prompt the following error:

class Person {
  // Member 'fullName' implicitly has an 'any' type.(7008)
  fullName; // Error
  firstName; // Error
  lastName; // Error

  constructor(fullName: string) {
    this.fullName = fullName;
    this.firstName = fullName.split("") [0];
    this.lastName =   fullName.split("") [1]; }}Copy the code

This feature makes it convenient to infer the type of a class attribute from the constructor. However, during use, if we cannot guarantee that all member attributes are assigned, then the attribute may be considered undefined.

class Person {
   fullName;  // (property) Person.fullName: string
   firstName; // (property) Person.firstName: string | undefined
   lastName; // (property) Person.lastName: string | undefined

   constructor(fullName: string) {
     this.fullName = fullName;
     if(Math.random()){
       this.firstName = fullName.split("") [0];
       this.lastName =   fullName.split("") [1]; }}}Copy the code

14.2 Tuple elements of tags

In the following example, we use the tuple type to declare the types of the remaining parameters:

function addPerson(. args: [string.number]) :void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`)
}

addPerson("lolo".5); // Person info: name: lolo, age: 5 
Copy the code

In fact, we can implement the addPerson function as follows:

function addPerson(name: string, age: number) {
  console.log(`Person info: name: ${name}, age: ${age}`)}Copy the code

The two approaches don’t look very different, but with the first approach, we can’t set the names of the first and second arguments. Although this has no effect on type checking, the lack of tags at tuple locations makes them difficult to use. To improve the developer experience with tuples, TypeScript 4.0 supports setting tags for tuple types:

function addPerson(. args: [name:string, age: number]) :void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`);
}
Copy the code

Later, when we use the addPerson method, TypeScript’s smart hints become more friendly.

// Intelligent hints for labels are not used
// addPerson(args_0: string, args_1: number): void
function addPerson(. args: [string.number]) :void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`)}// Smart hints for labels used
// addPerson(name: string, age: number): void
function addPerson(. args: [name:string, age: number]) :void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`);
} 
Copy the code

Xv. Compile context

15.1 Functions of tsconfig.json

  • The root path that identifies a TypeScript project;
  • Configure the TypeScript compiler;
  • Used to specify the file to compile.

15.2 tsconfig.json Important fields

  • Files – Sets the name of the file to compile;
  • Include – sets the files to be compiled and supports path pattern matching.
  • Exclude – Sets the files that do not need to be compiled to support path pattern matching.
  • CompilerOptions – Sets the options related to the compile process.

15.3 compilerOptions options

CompilerOptions supports many options, such as baseUrl, Target, baseUrl, moduleResolution, and lib.

The details for each option are as follows:

{
  "compilerOptions": {

    /* Basic options */
    "target": "es5".// Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs".// Specify the module to use: 'commonjs', 'AMD ', 'system', 'umd' or 'es2015'
    "lib": [].// Specifies the library files to include in the compilation
    "allowJs": true.// Allow compilation of javascript files
    "checkJs": true.// Report the error in the javascript file
    "jsx": "preserve".// Specify JSX code generation: 'preserve', 'react native', or 'react'
    "declaration": true.// Generate the corresponding '.d.ts' file
    "sourceMap": true.// Generate the corresponding '.map' file
    "outFile": ". /".// Merge the output files into one file
    "outDir": ". /".// Specify the output directory
    "rootDir": ". /".// To control the output directory structure --outDir.
    "removeComments": true.// Delete all compiled comments
    "noEmit": true.// No output file is generated
    "importHelpers": true.// Import helper functions from tslib
    "isolatedModules": true.// Treat each file as a separate module (similar to 'ts.transpilemodule').

    /* Strict type checking options */
    "strict": true.// Enable all strict type checking options
    "noImplicitAny": true.// There is an implicit error in expressions and declarations with the type any
    "strictNullChecks": true.// Enable strict NULL checking
    "noImplicitThis": true.// Generate an error when this expression is of type any
    "alwaysStrict": true.// Check each module in strict mode and put 'use strict' in each file

    /* Additional checks */
    "noUnusedLocals": true.// Throw an error when there is an unused variable
    "noUnusedParameters": true.// Throw an error when there are unused arguments
    "noImplicitReturns": true.// Throw an error when not all the code in a function returns a value
    "noFallthroughCasesInSwitch": true.// Report fallthrough error of switch statement. (that is, do not allow the case statement of the switch to run through)

    /* Module parsing options */
    "moduleResolution": "node".// Select a module resolution strategy: 'node' (node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": ". /".// Used to resolve the base directory for non-relative module names
    "paths": {},                           // List of module names to baseUrl based path mappings
    "rootDirs": [].// A list of root folders whose combined content represents the structured content of the project at runtime
    "typeRoots": [].// A list of files containing type declarations
    "types": [].// A list of type declaration filenames to include
    "allowSyntheticDefaultImports": true.// Allow a default import from a module that has no default export set.

    /* Source Map Options */
    "sourceRoot": ". /".// Specifies where the debugger should find TypeScript files instead of source files
    "mapRoot": ". /".// Specifies where the debugger should find the mapping file instead of the build file
    "inlineSourceMap": true.// Generate a single soucemaps file instead of generating separate sourcemaps files
    "inlineSources": true.// Generate code and sourcemaps in a file, requiring that either the --inlineSourceMap or --sourceMap properties are set

    /* Other options */
    "experimentalDecorators": true.// Enable decorators
    "emitDecoratorMetadata": true          // Provide metadata support for decorators}}Copy the code

TypeScript development AIDS

16.1 TypeScript Playground

Introduction: TypeScript’s official online TypeScript runtime environment allows you to learn about TypeScript and the features of different versions of TypeScript.

Online address: www.typescriptlang.org/play/

In addition to TypeScript’s official Playground, there are other playgrounds to choose from, such as codepen. IO, Stackblitz, or jsbin.com.

16.2 TypeScript UML Playground

Introduction: An online TypeScript UML tool that allows you to generate UML class diagrams for specified TypeScript code.

Online address: tsuml-demo.firebaseapp.com/

16.3 JSON TO TS

Introduction: aN online TypeScript tool that allows you to generate TypeScript interface definitions for specified JSON data.

Online address: www.jsontots.com/

In addition to using the JsonTots online tool, you can also install the JSON to TS extension to quickly convert JSON to TS for VSCode IDE users.

16.4 Schemats

Introduction: Schemats allows you to automatically generate TypeScript interface definitions based on schemas in SQL databases (Postgres, MySQL).

Online address: github.com/SweetIQ/sch…

16.5 TypeScript AST Viewer

An online TypeScript AST tool that allows you to view the Abstract Syntax Tree (AST) of specified TypeScript code.

Online address: TS-COMMENCEMENT viewer.com/

For those of you who know AST, astExplorer is an online tool that should be familiar. In addition to JavaScript support, the tool also supports parsing of CSS, JSON, RegExp, GraphQL, and Markdown formats.

16.6 TypeDoc

Introduction: TypeDoc is used to convert comments in TypeScript source code into HTML documents or JSON models. It is flexible and extensible and supports a variety of configurations.

Online address: typedoc.org/

16.7 TypeScript ESLint

Introduction: Using TypeScript ESLint can help you standardize code quality and improve team development efficiency.

Online address: typescript-eslint. IO /

For those interested in using “ESLint and Prettier” on TypeScript projects, see “How to gracefully Use” ESLint and “Prettier” on TypeScript Projects.

If you want more, check out my 1.8K+ open source project on Github: Awesome -typescript.

17. Reference Resources

  • mariusschulz – the-unknown-type-in-typescript
  • In-depth understanding of typescript-compilation context
  • TypeScript 4.0
  • TypeScript Quickly