八、TypeScript数组
8.1 数组解构
let x: number; let y: number; let z: number;
let five_array = [0,1,2,3,4];
[x,y,z] = five_array;
8.2 数组展开运算符
let two_array = [0, 1];
let five_array = [...two_array, 2, 3, 4];
8.3 数组遍历
let colors: string[] = ["red", "green", "blue"];
for (let i of colors) {
console.log(i);
}
九、TypeScript对象
9.1 对象解构
let person = {
name: "btqf",
gender: "Male",
};
let { name, gender } = person;
9.2 对象展开运算符
let person = {
name: "btqf",
gender: "Male",
address: "Xiamen",
};
// 组装对象
let personWithAge = { ...person, age: 33 };
// 获取除了某些项外的其它项
let { name, ...rest } = person;
十、TypeScript 接口
在面向对象语言中,接口是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类去实现。TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
10.1 对象的形状
interface Person {
name: string;
age: number;
}
let btqf: Person = {
name: 'btqf';
age: 22;
}
10.2 可选 | 只读属性
interface Person {
readonly name: string;
age?: number;
}
10.3 任意属性
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
const p1 = { name: "btqf" };
const p2 = { name: "lolo", age: 5 };
const p3 = { name: "kakuqo", sex: 1 }
10.4 Declaration merging
接口可以定义多次,会被自动合并为单个接口。
interface Point { x: number; }
interface Point { y: number; }
const point: Point = { x: 1, y: 2 };
十一、TypeScript 类
在面向对象语言中,类是一种面向对象计算机编程语言的构造,是创建对象的蓝图,描述了所创建的对象共同的属性和方法
11.1 类的属性与方法
class Greeter {
// 静态属性
static cname: string = "Greeter";
// 成员属性:在原型链上
greeting: string;
// 构造函数 - 执行初始化操作
constructor(message: string) {
this.greeting = message;
}
// 静态方法
static getClassName() {
return "Class name is Greeter";
}
// 成员方法
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
11.2 ECMAScript 私有字段
class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
greet() {
console.log(`Hello, my name is ${this.#name}!`);
}
}
let semlinker = new Person("btqf");
semlinker.#name;
私有字段存在以下规则:
- 私有字段以
#
字符开头,有时我们称之为私有名称 - 每个私有字段都唯一地限定其包含的类,不能被包含的类之外访问
- 不能在私有字段上使用TypeScript可访问修饰符(如public / private)
11.3 访问器
通过setter
和getter
实现数据的封装和有效性检验,防止出现异常数据 ```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); }
<a name="LjeGj"></a>
### 11.4 类的继承
通过`extends`关键字来实现继承
```javascript
class Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) {
super(name); // 调用父类的构造函数
}
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
sam.move();
11.5 抽象类
使用 abstract
关键字声明的类,我们称之为抽象类。抽象类不能被实例化,因为它里面包含一个或多个抽象方法。所谓的抽象方法,是指不包含具体实现的方法。
抽象类不能被直接实例化,我们只能实例化实现了所有抽象方法的子类。
abstract class Person {
constructor(public name: string){}
// 抽象方法
abstract say(words: string) :void;
}
class Developer extends Person {
constructor(name: string) {
super(name);
}
say(words: string): void {
console.log(`${this.name} says ${words}`);
}
}
const lolo = new Developer("lolo");
lolo.say("I love ts!"); // lolo says I love ts!
十二、TypeScript泛型
在像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。
泛型(Generics)是允许同一个函数接受不同类型参数的一种模板。相比于使用 any 类型,使用泛型来创建可复用的组件要更好,因为泛型会保留参数类型。
12.1 泛型语法
function identity <T, U>(value: T, message: U) : T {
console.log(message);
return value;
}
console.log(identity<Number, string>(68, "Semlinker"));
console.log(identity(68, "Semlinker")); // 编译器自动选择类型,省略尖括号
看到上述的代码,我们在定义identity
函数时使用了 <T, U>
,这其实没有什么特别的。它像传递参数一眼,我们传递了我们想要用于特定函数调用的类型。
其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:
- K(Key):表示对象中的键类型;
- V(Value):表示对象中的值类型;
- E(Element):表示元素类型。
12.2 泛型接口
interface GenericIdentityFn<T> {
(arg:T) : T;
}
12.3 泛型类
```javascript class GenericNumber{ zeroValue: T; add: (x: T, y: T) => T; }
let myGenericNumber = new GenericNumber
<a name="rPjP6"></a>
### 12.4 泛型工具类型
为了方便开发者 TypeScript 内置了一些常用的工具类型,比如 Partial、Required、Readonly、Record 和 ReturnType 等。这里我们只简单介绍 Partial 工具类型。
1. **相关的基础知识**
`typeof`/`keyof`/`in`/`infer`/`extends`
2. **Partial**
`Partial<T>`的作用就是将某个类型里的属性全部变为可选项`?`.
```javascript
type Partial<T> = {
[P in keyof T]?: T[P];
};
在上面的代码中,首先通过keyof T
拿到T
的所有属性名,然后用in
进行遍历,将值赋给P
,最后通过T[P]
取得相应的属性值。中间的?
号,用于将所有属性变为可选。
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",
});
在上面的updateTodo
方法中,我们利用Partial<T>
工具类型,定义fieldsToUpdate
的类型为Partial<Todo>
,即:
{
title?: string | undefined;
description?: string | undefined;
}
十三、TypeScript装饰器
13.1 装饰器是什么
- 它是一个表达式
- 该表达式被执行后,返回一个函数
- 函数的入参分别为target/name/descriptor
执行该函数,可能返回descriptor对象,用于配置target对象
13.2 装饰器的分类
类装饰器
- 属性装饰器
- 方法装饰器
-
13.3 类装饰器
类装饰器声明:
declare type ClassDecorator = <TFunction extends Function>(
target: TFunction
) => TFunction | void;
类装饰器顾名思义,就是用来装饰类的。它接收一个参数:
target: TFunction - 被装饰的类 ```javascript function Greeter(greeting: string) { return function (target: Function) { target.prototype.greet = function (): void {
console.log(greeting);
}; }; }
@Greeter(“Hello TS!”) class Greeting { constructor() { // 内部实现 } }
let myGreeting = new Greeting(); (myGreeting as any).greet(); // console output: ‘Hello TS!’;
在上面的例子中,我们定义了`Greeter`类装饰器,同时我们使用了`@Greeter`语法糖,来使用装饰器。
<a name="l0fMh"></a>
### 13.4 属性装饰器
属性装饰器声明:
```javascript
declare type PropertyDecorator = (target:Object,
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”;
以上代码我们定义了一个 logProperty 函数,来跟踪用户对属性的操作,当代码成功运行后,在控制台会输出以下结果:
```javascript
Set: name => semlinker
Set: name => kakuqo
13.5 方法装饰器
方法装饰器声明:
declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol,
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);
以上代码成功运行后,控制台会输出以下结果:
```javascript
"wrapped function: before invoking runTask"
"runTask invoked, args: learn ts"
"wrapped function: after invoking runTask"
"result: finished"
13.6 参数装饰器
参数装饰器声明:
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol,
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; } }
以上代码成功运行后,控制台会输出一下结果:
```javascript
"The parameter in position 0 at Greeter has been decorated"