八、TypeScript数组

8.1 数组解构

  1. let x: number; let y: number; let z: number;
  2. let five_array = [0,1,2,3,4];
  3. [x,y,z] = five_array;

8.2 数组展开运算符

  1. let two_array = [0, 1];
  2. let five_array = [...two_array, 2, 3, 4];

8.3 数组遍历

  1. let colors: string[] = ["red", "green", "blue"];
  2. for (let i of colors) {
  3. console.log(i);
  4. }

九、TypeScript对象

9.1 对象解构

  1. let person = {
  2. name: "btqf",
  3. gender: "Male",
  4. };
  5. let { name, gender } = person;

9.2 对象展开运算符

  1. let person = {
  2. name: "btqf",
  3. gender: "Male",
  4. address: "Xiamen",
  5. };
  6. // 组装对象
  7. let personWithAge = { ...person, age: 33 };
  8. // 获取除了某些项外的其它项
  9. let { name, ...rest } = person;

十、TypeScript 接口

在面向对象语言中,接口是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类去实现。TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

10.1 对象的形状

  1. interface Person {
  2. name: string;
  3. age: number;
  4. }
  5. let btqf: Person = {
  6. name: 'btqf';
  7. age: 22;
  8. }

10.2 可选 | 只读属性

  1. interface Person {
  2. readonly name: string;
  3. age?: number;
  4. }

10.3 任意属性

  1. interface Person {
  2. name: string;
  3. age?: number;
  4. [propName: string]: any;
  5. }
  6. const p1 = { name: "btqf" };
  7. const p2 = { name: "lolo", age: 5 };
  8. const p3 = { name: "kakuqo", sex: 1 }

10.4 Declaration merging

接口可以定义多次,会被自动合并为单个接口。

  1. interface Point { x: number; }
  2. interface Point { y: number; }
  3. const point: Point = { x: 1, y: 2 };

十一、TypeScript 类

在面向对象语言中,类是一种面向对象计算机编程语言的构造,是创建对象的蓝图,描述了所创建的对象共同的属性和方法

11.1 类的属性与方法

  1. class Greeter {
  2. // 静态属性
  3. static cname: string = "Greeter";
  4. // 成员属性:在原型链上
  5. greeting: string;
  6. // 构造函数 - 执行初始化操作
  7. constructor(message: string) {
  8. this.greeting = message;
  9. }
  10. // 静态方法
  11. static getClassName() {
  12. return "Class name is Greeter";
  13. }
  14. // 成员方法
  15. greet() {
  16. return "Hello, " + this.greeting;
  17. }
  18. }
  19. let greeter = new Greeter("world");

11.2 ECMAScript 私有字段

  1. class Person {
  2. #name: string;
  3. constructor(name: string) {
  4. this.#name = name;
  5. }
  6. greet() {
  7. console.log(`Hello, my name is ${this.#name}!`);
  8. }
  9. }
  10. let semlinker = new Person("btqf");
  11. semlinker.#name;

私有字段存在以下规则:

  • 私有字段以#字符开头,有时我们称之为私有名称
  • 每个私有字段都唯一地限定其包含的类,不能被包含的类之外访问
  • 不能在私有字段上使用TypeScript可访问修饰符(如public / private)

    11.3 访问器

    通过settergetter实现数据的封装和有效性检验,防止出现异常数据 ```javascript 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 = “btqf”; if (employee.fullName) { console.log(employee.fullName); }

  1. <a name="LjeGj"></a>
  2. ### 11.4 类的继承
  3. 通过`extends`关键字来实现继承
  4. ```javascript
  5. class Animal {
  6. name: string;
  7. constructor(theName: string) {
  8. this.name = theName;
  9. }
  10. move(distanceInMeters: number = 0) {
  11. console.log(`${this.name} moved ${distanceInMeters}m.`);
  12. }
  13. }
  14. class Snake extends Animal {
  15. constructor(name: string) {
  16. super(name); // 调用父类的构造函数
  17. }
  18. move(distanceInMeters = 5) {
  19. console.log("Slithering...");
  20. super.move(distanceInMeters);
  21. }
  22. }
  23. let sam = new Snake("Sammy the Python");
  24. sam.move();

11.5 抽象类

使用 abstract关键字声明的类,我们称之为抽象类。抽象类不能被实例化,因为它里面包含一个或多个抽象方法。所谓的抽象方法,是指不包含具体实现的方法。
抽象类不能被直接实例化,我们只能实例化实现了所有抽象方法的子类。

  1. abstract class Person {
  2. constructor(public name: string){}
  3. // 抽象方法
  4. abstract say(words: string) :void;
  5. }
  6. class Developer extends Person {
  7. constructor(name: string) {
  8. super(name);
  9. }
  10. say(words: string): void {
  11. console.log(`${this.name} says ${words}`);
  12. }
  13. }
  14. const lolo = new Developer("lolo");
  15. lolo.say("I love ts!"); // lolo says I love ts!

十二、TypeScript泛型

在像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。
泛型(Generics)是允许同一个函数接受不同类型参数的一种模板。相比于使用 any 类型,使用泛型来创建可复用的组件要更好,因为泛型会保留参数类型。

12.1 泛型语法

  1. function identity <T, U>(value: T, message: U) : T {
  2. console.log(message);
  3. return value;
  4. }
  5. console.log(identity<Number, string>(68, "Semlinker"));
  6. console.log(identity(68, "Semlinker")); // 编译器自动选择类型,省略尖括号

看到上述的代码,我们在定义identity函数时使用了 <T, U>,这其实没有什么特别的。它像传递参数一眼,我们传递了我们想要用于特定函数调用的类型。
其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:

  • K(Key):表示对象中的键类型;
  • V(Value):表示对象中的值类型;
  • E(Element):表示元素类型。

    12.2 泛型接口

    1. interface GenericIdentityFn<T> {
    2. (arg:T) : T;
    3. }

    12.3 泛型类

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

let myGenericNumber = new GenericNumber(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function (x, y) { return x + y; };

  1. <a name="rPjP6"></a>
  2. ### 12.4 泛型工具类型
  3. 为了方便开发者 TypeScript 内置了一些常用的工具类型,比如 Partial、Required、Readonly、Record 和 ReturnType 等。这里我们只简单介绍 Partial 工具类型。
  4. 1. **相关的基础知识**
  5. `typeof`/`keyof`/`in`/`infer`/`extends`
  6. 2. **Partial**
  7. `Partial<T>`的作用就是将某个类型里的属性全部变为可选项`?`.
  8. ```javascript
  9. type Partial<T> = {
  10. [P in keyof T]?: T[P];
  11. };

在上面的代码中,首先通过keyof T拿到T的所有属性名,然后用in进行遍历,将值赋给P,最后通过T[P]取得相应的属性值。中间的号,用于将所有属性变为可选。

  1. interface Todo {
  2. title: string;
  3. description: string;
  4. }
  5. function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  6. return { ...todo, ...fieldsToUpdate };
  7. }
  8. const todo1 = {
  9. title: "Learn TS",
  10. description: "Learn TypeScript",
  11. };
  12. const todo2 = updateTodo(todo1, {
  13. description: "Learn TypeScript Enum",
  14. });

在上面的updateTodo方法中,我们利用Partial<T>工具类型,定义fieldsToUpdate的类型为Partial<Todo>,即:

  1. {
  2. title?: string | undefined;
  3. description?: string | undefined;
  4. }

十三、TypeScript装饰器

13.1 装饰器是什么

  • 它是一个表达式
  • 该表达式被执行后,返回一个函数
  • 函数的入参分别为target/name/descriptor
  • 执行该函数,可能返回descriptor对象,用于配置target对象

    13.2 装饰器的分类

  • 类装饰器

  • 属性装饰器
  • 方法装饰器
  • 参数装饰器

    13.3 类装饰器

    类装饰器声明:

    1. declare type ClassDecorator = <TFunction extends Function>(
    2. target: TFunction
    3. ) => TFunction | void;

    类装饰器顾名思义,就是用来装饰类的。它接收一个参数:

  • target: TFunction - 被装饰的类 ```javascript function Greeter(greeting: string) { return function (target: Function) { target.prototype.greet = function (): void {

    1. console.log(greeting);

    }; }; }

@Greeter(“Hello TS!”) class Greeting { constructor() { // 内部实现 } }

let myGreeting = new Greeting(); (myGreeting as any).greet(); // console output: ‘Hello TS!’;

  1. 在上面的例子中,我们定义了`Greeter`类装饰器,同时我们使用了`@Greeter`语法糖,来使用装饰器。
  2. <a name="l0fMh"></a>
  3. ### 13.4 属性装饰器
  4. 属性装饰器声明:
  5. ```javascript
  6. declare type PropertyDecorator = (target:Object,
  7. propertyKey: string | symbol ) => void;

属性装饰器顾名思义,用来装饰类的属性。它接收两个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 被装饰类的属性名 ```javascript 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”;

  1. 以上代码我们定义了一个 logProperty 函数,来跟踪用户对属性的操作,当代码成功运行后,在控制台会输出以下结果:
  2. ```javascript
  3. Set: name => semlinker
  4. Set: name => kakuqo

13.5 方法装饰器

方法装饰器声明:

  1. declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol,
  2. descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;

方法装饰器顾名思义,用来装饰类的方法。它接收三个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 方法名
  • descriptor: TypePropertyDescript - 属性描述符 ```javascript 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);

  1. 以上代码成功运行后,控制台会输出以下结果:
  2. ```javascript
  3. "wrapped function: before invoking runTask"
  4. "runTask invoked, args: learn ts"
  5. "wrapped function: after invoking runTask"
  6. "result: finished"

13.6 参数装饰器

参数装饰器声明:

  1. declare type ParameterDecorator = (target: Object, propertyKey: string | symbol,
  2. parameterIndex: number ) => void

参数装饰器顾名思义,是用来装饰函数参数,它接收三个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 方法名
  • parameterIndex: number - 方法中参数的索引值 ``javascript 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; } }

  1. 以上代码成功运行后,控制台会输出一下结果:
  2. ```javascript
  3. "The parameter in position 0 at Greeter has been decorated"