0%

TS入门

定义

​ TypeScript is a typed(静态类型,不同于JS的动态类型) superset of JavaScript that compiles to plain JavaScript(TS不能直接运行,需要先编译成JS).Any browser. Any host. Any OS. Open source.

好处

  1. 开发过程中就会发现错误,比如下面的代码JS中显示没问题,TS中就会提示你有潜在的问题,JS只有在编译之后才发现问题
1
2
3
4
function demo(data) {
return Math.sqrt(data.x ** 2 + data.y ** 2);
}
demo({x:3,y:'4'});
1
2
3
4
function demo(data: { x: number; y: number }) {
return Math.sqrt(data.x ** 2 + data.y ** 2);
}
demo({x:3,y:'4'});
  1. 代码提示
  2. 可读性更好

开发环境

ts-node可以直接运行TS文件,ts-node xxx.ts,避免先tsc xxx.ts,再node xxx.js。

静态类型深度理解

1
const count: number = 2020;

当count具备number的静态类型之后,count会具备number这个类型的所有属性与方法。

1.基础语法

1.1基础类型与对象类型

基础类型

number、boolean、string、null、undefined、symbol、void

对象类型

对象类型{}、数组类型[]、类类型、函数类型

1.2类型注解和类型推断

type annotation

类型注解。我们来告诉TS变量什么类型

type inference

类型推断。TS会自动的去分析变量的类型,如果TS无法分析变量类型,我们就需要使用类型注解。

对于解构赋值的类型注解

1
2
3
4
function add({ first, second }: { first: number; second: number }): number {
return first + second;
}
const total = add({ first: 1, second: 2 });

赋值和变量的定义在一行的话,类型推断可以有效果。否则的话,类型推断无法推断出来。

1
2
3
4
let count;
count = 123;
-----------------
let count = 123;
1
2
3
4
5
6
7
8
const fun1 = (str: string): number => {
return parseInt(str, 10);
};
//上面一种方式如果类型推断能推断出函数的返回值,那么number可以省略。
//但下面一种方式number省略的话,语法就不对了。
const fun2: (str: string) => number = str => {
return parseInt(str, 10);
};

1.3数组与元组

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const list: number[] = [1, 2, 3];
const arr: (number|string)[] = [1, '2', 3];
const undefinedArr: undefined[] = [undefined];
const objectArr: {name: string}[] = [{name: 'zj'}]
//类型别名
type User = {name: string,age: number};
const obj1Arr: User[] = [{name: 'zj', age: 1}];

class Teacher {
name: string;
age: number;
}
const obj2Arr: Teacher[] = [
{
name: 'zj',
age: 1
},
new Teacher()
]
1
let list: Array<number> = [1, 2, 3];

元组

1
2
3
4
5
6
7
8
9
10
11
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
//csv execl导出的文件常用元组
const list: [number, number, string][] = [
[1, 2, 'a'],
[2, 3, 'b']
]

1.4接口

接口定义属性、方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface Person {
//readonly name: string; //只读
name: string;
age?: number; //可选
}
//类型别名也能起到interface的效果,区别是类型别名后面可以是一个基础类型,比如
//type Person1 = string;
type Person1 = {
name: string;
};
const getPersonName = (person: Person) => {
console.log(person.name);
};
const setPersonName = (person: Person, name: string) => {
person.name = name;
};


const person = {
name: 'zj',
sex: 'male'
};
getPersonName(person);
//上面不会报错,即使你接口中没有声明sex,但是你声明必须有的name属性满足
getPersonName({
name: 'zj',
sex: 'male'
});
//报错,直接以字面量的形式传递给一个变量的时候,会进行强校验,sex是未声明的
1
2
3
4
5
6
7
interface Person {
//readonly name: string; //只读
name: string;
age?: number; //可选
[propName: string]: any; //除了必传的name,可以有其他属性,只要属性名是string类型就ok~
say(): string; //函数,返回值类型string
}

接口定义函数

1
2
3
4
5
6
interface SayHi {
(word: string): string;
}
const say: SayHi = (word: string) => {
return word;
};

类实现接口

1
2
3
4
5
6
7
8
9
10
11
interface Person {
name: string;
age?: number;
say(): string;
}
class User implements Person {
name: 'zj';
say() {
return 'hello';
}
}

接口继承接口

1
2
3
interface Teacher extends Person {
teach(): string;
}

1.5类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person {
name: string = 'zhou';
getName() {
return this.name;
}
}

class Teacher extends Person {
getTeacherName() {
return 'jun';
}
getName() {
//重写父类
return super.getName() + this.getTeacherName();
//如果父类的方法被覆盖,但是还想调用父类的方法,可以用super。
}
}

const person = new Person();
const teacher = new Teacher();
console.log(teacher.getName());
console.log(teacher.getTeacherName());

修饰符

  1. public

    允许在类的内外调用

  2. private

    允许在类内调用

  3. protected

    允许在类内及继承的子类调用

  4. readonly

构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//传统写法
class Person {
public name: string = 'zhou';
constructor(name: string) {
this.name = name;
}
}
const person = new Person('jun');
console.log(person.name);
-----------------------------------------
//简化写法
class Person {
constructor(public name: string) {}
}
const person = new Person('jun');
console.log(person.name);
1
2
3
4
5
6
7
8
9
class Person {
constructor(public name: string) {}
}
class Teacher extends Person {
constructor(public age: number) {
super('zhou'); //执行父类的构造函数
}
}
const teacher = new Teacher(28);

get和set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
constructor(private _name: string) {}
get name() {
return this._name + ' jun';
}
set name(name: string) {
this._name = name;
}
}

const person = new Person('zhou');
console.log(person.name);
person.name = 'hello';
console.log(person.name);

静态属性

挂载在类的本身上,而不是类的实例上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}

let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale

console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

单例模式

1
2
3
4
5
6
7
8
9
10
11
12
class Single {
private static instance: Single; //why static? 此时并没有实例被创建,所以要直接挂载在类上
private constructor() {}
static getInstance() {
if (!this.instance) Single.instance = new Single();
return Single.instance;
}
}

const demo1 = Single.getInstance();
const demo2 = Single.getInstance();
console.log(demo1 === demo2);//true

抽象类

抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。

抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含abstract关键字并且可以包含访问修饰符。

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Geom {
width: number = 0;
getType() {
return 'Gemo';
}
abstract getArea(): number;
}

class Circle extends Geom {
getArea() {
return 123;
}
}

1.6联合类型和类型保护

1
2
3
4
5
6
7
8
9
interface Bird {
fly: boolean;
sing: () => {};
}

interface Dog {
fly: boolean;
bark: () => {};
}

类型断言的方式进行类型保护

1
2
3
4
5
6
7
function trainAnimal(animal: Bird | Dog) {
if (animal.fly) {
(animal as Bird).sing();
} else {
(animal as Dog).bark();
}
}

in 语法来做类型保护

1
2
3
4
function trainAnimal(animal: Bird | Dog) {
if ('sing' in animal) animal.sing();
else animal.bark();
}

typeof语法来做类型保护

1
2
3
4
5
6
function add(first: string | number, second: string | number) {
if (typeof first === 'string' || typeof second === 'string') {
return `${first}${second}`;
}
return first + second;
}

使用instanceof语法来做类型保护

1
2
3
4
5
6
7
8
9
class NumberObj {
count: number;
}
function add1(first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count;
}
return 0;
}

1.7枚举类型

1
2
3
4
enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c); // 输出 2
console.log(Color[0]); //输出Red

1.8泛型

generic

函数泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function join<T>(first: T, second: T) {
return `${first}${second}`;
}
function map<T>(params: T[]) {
return params;
}
function anotherJoin<T, P>(first: T, second: P) {
return `${first}${second}`;
}

join<string>('1', '1');
map<string>(['123']);
anotherJoin<number, string>(1, '123');
anotherJoin('123',1);
//function join2<string, number>(first: string, second: number): string
//如果你没有显式的告诉T、P是啥类型的话,TS会根据你传的值进行类型推断

泛型类

1
2
3
4
5
6
7
8
9
class DataManager<T> {
constructor(private data: T[]) {}
getItem(index: number): T {
return this.data[index];
}
}

const data = new DataManager<string>(['1', '2']);
data.getItem(0);

泛型约束

1
2
3
4
5
6
7
8
9
10
11
interface Item {
name: string;
}
class DataManager<T extends Item> {
constructor(private data: T[]) {}
getItem(index: number): string {
return this.data[index].name;
}
}

const data = new DataManager([{ name: 'zhou' }]);

keyof的使用

类型可以是字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface Person {
name: string;
age: number;
gender: string;
}
// type T = 'name'
// key: 'name'
// Person['name']
// type T = 'age'
// key: 'age'
// Person['age']
// type T = 'gender'
// key: 'gender'
// Person['gender']
class Teacher {
constructor(private info: Person) {}
getInfo<T extends keyof Person>(key: T): Person[T] {
return this.info[key];
}
}

const teacher = new Teacher({
name: 'zhou',
age: 1,
gender: 'male'
});

const test = teacher.getInfo('name');

2.模块化

2.1命名空间namespace

尽可能少的生成全局变量,把必须暴露出去的export出去,其他的通过namespace封装起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace Home {
class Header {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is header';
document.body.appendChild(elem);
}
}

class Content {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is content';
document.body.appendChild(elem);
}
}

class Footer {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is footer';
document.body.appendChild(elem);
}
}

export class Page {
constructor() {
new Header();
new Content();
new Footer();
}
}
}

模块拆分以及打包到一个文件

components.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
namespace Components {
export class Header {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is header';
document.body.appendChild(elem);
}
}

export class Content {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is content';
document.body.appendChild(elem);
}
}

export class Footer {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is footer';
document.body.appendChild(elem);
}
}
}

page.ts

///<reference path="./components.ts" />Home命名空间依赖于Components命名空间

1
2
3
4
5
6
7
8
9
10
11
///<reference path="./components.ts" />

namespace Home {
export class Page {
constructor() {
new Components.Header();
new Components.Content();
new Components.Footer();
}
}
}

通过tsc命令并且修改tsconfig.json文件下的:

  • "rootDir": "./src":需要打包的目录

  • "outFile": "./dist/page.js":打包到哪个文件

  • ``”module”: “amd”`

    Only ‘AMD’ and ‘System’ can be used in conjunction with —outFile.

最终打包成一个js文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
"use strict";
var Components;
(function (Components) {
var Header = /** @class */ (function () {
function Header() {
var elem = document.createElement('div');
elem.innerText = 'this is header';
document.body.appendChild(elem);
}
return Header;
}());
Components.Header = Header;
var Content = /** @class */ (function () {
function Content() {
var elem = document.createElement('div');
elem.innerText = 'this is content';
document.body.appendChild(elem);
}
return Content;
}());
Components.Content = Content;
var Footer = /** @class */ (function () {
function Footer() {
var elem = document.createElement('div');
elem.innerText = 'this is footer';
document.body.appendChild(elem);
}
return Footer;
}());
Components.Footer = Footer;
})(Components || (Components = {}));
///<reference path="./components.ts" />
var Home;
(function (Home) {
var Page = /** @class */ (function () {
function Page() {
new Components.Header();
new Components.Content();
new Components.Footer();
}
return Page;
}());
Home.Page = Page;
})(Home || (Home = {}));

namespace也可以嵌套使用

2.2import

components.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export class Header {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is header';
document.body.appendChild(elem);
}
}

export class Content {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is content';
document.body.appendChild(elem);
}
}

export class Footer {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'this is footer';
document.body.appendChild(elem);
}
}

page.ts

1
2
3
4
5
6
7
8
9
import { Header, Content, Footer } from './components';

export class Page {
constructor() {
new Header();
new Content();
new Footer();
}
}

index.html

需要引入require.js,识别打包成的js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js"></script>
<script src="./dist/page.js"></script>
</head>

<body>
<script>
require(['page'], function (page) {
new page.Page()
})
</script>
</body>

</html>

npm install parcel@next

使用Parcel打包TS代码

3.编写类型描述文件.d.ts

.d.ts是为了帮助TS文件更好的理解我们引入的JS文件

3.1全局类型

page.ts文件

1
2
3
4
$(function() {
$('body').html('<div>123</div>');
new $.fn.init();
});

juery.d.ts文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//定义全局变量
// declare var $: (params: () => void) => void;

//定义全局函数
interface JqueryInstance {
html: (html: string) => JqueryInstance;
}

//同一个函数名可以定义多个全局函数(函数重载)
declare function $(readyFunc: () => void): void;
declare function $(selector: string): JqueryInstance;

//如何对对象进行类型定义,以及对类进行类型定义,以及命名空间的嵌套
declare namespace $ {
namespace fn {
class init {}
}
}

// 使用interface的语法,实现函数重载
// interface JQuery {
// (readyFunc: () => void): void;
// (selector: string): JqueryInstance;
// }

// declare var $: JQuery;

3.2模块化变量

ES6

page.ts文件

1
2
3
4
5
6
import $ from 'jquery';

$(function() {
$('body').html('<div>123</div>');
new $.fn.init();
});

juery.d.ts文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
declare module 'jquery' {
interface JqueryInstance {
html: (html: string) => JqueryInstance;
}

//混合类型
function $(readyFunc: () => void): void;
function $(selector: string): JqueryInstance;
namespace $ {
namespace fn {
class init {}
}
}
export = $;
}

4.高级语法

4.1类的装饰器

  • 装饰器本身是一个函数
  • 类装饰器接受的是构造函数
  • 装饰器执行在类创建的时候

装饰器工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
function testDecorator() { //这是一个装饰器工厂
return function(constructor: any) { //这是装饰器
constructor.prototype.getName = () => {
console.log('zhou');
};
};
}

@testDecorator()
class Test {}

const test = new Test();
(test as any).getName();

组合装饰器

当多个装饰器应用在一个声明上时会进行如下步骤的操作:

  1. 由上至下依次对装饰器表达式求值
  2. 求值的结果会被当作函数,由下至上依次调用
1
2
3
4
5
6
7
8
9
10
11
12
13
function testDecorator1(constructor: any) {
console.log('decorator1');
}

function testDecorator2(constructor: any) {
console.log('decorator2');
}

@testDecorator1
@testDecorator2
class Test {}
//decorator2
//decorator1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function testDecorator1() {
console.log('decorator1 evaluated');
return function(constructor: any) {
console.log('decorator1 called');
};
}

function testDecorator2() {
console.log('decorator2 evaluated');
return function(constructor: any) {
console.log('decorator2 called');
};
}

@testDecorator1()
@testDecorator2()
class Test {}
//decorator1 evaluated
//decorator2 evaluated
//decorator2 called
//decorator1 called
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function testDecorator() {
return function<T extends new (...args: any[]) => {}>(constructor: T) {
return class extends constructor {
name = 'jun';
getName() {
return this.name;
}
};
};
}

const Test = testDecorator()(
class {
name: string;
constructor(name: string) {
this.name = name;
}
}
);

const test = new Test('zhou');
console.log(test);

4.2方法装饰器

方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符

类似于Object.defineProperties()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function getNameDecorator(
target: any,
key: string,
descriptor: PropertyDescriptor
) {
// console.log(target);
// descriptor.writable = false;
descriptor.value = () => {
return 'decorator';
};
}

class Test {
name: string;
constructor(name: string) {
this.name = name;
}
@getNameDecorator
getName() {
return this.name;
}
}

const test = new Test('zhou');
console.log(test.getName());

4.3访问器装饰器

不允许同时装饰一个成员的getset访问器。取而代之的是,一个成员的所有装饰器必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个属性描述符时,它联合了getset访问器,而不是分开声明的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function visitDecorator(
target: any,
key: string,
descriptor: PropertyDescriptor
) {
// descriptor.writable = false;
}


class Test {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name() {
return this._name;
}
@visitDecorator
set name(name: string) {
this._name = name;
}
}

const test = new Test('zhou');
test.name = '12312312';
console.log(test.name);

4.4属性装饰器

属性装饰器改变属性descriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function visitDecorator(target: any, key: string): any {
const descriptor: PropertyDescriptor = {
writable: false
};
return descriptor;
}

class Test {
@visitDecorator
name = 'zhou';
}

const test = new Test();
test.name = 'jun';
console.log(test.name);
1
2
3
4
5
6
7
8
9
10
11
12
function visitDecorator(target: any, key: string): any {
target[key] = 'jun'; //修改的并不是实例上的name,而是原型上的name
}

//name放在实例上
class Test {
@visitDecorator
name = 'zhou';
}

const test = new Test();
console.log(test.name); // zhou

4.5参数装饰器

参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 参数在函数参数列表中的索引。
1
2
3
4
5
6
7
8
9
10
11
12
13
function paramDecorator(target: any, method: string, paramIndex: number) {
console.log(target, method, paramIndex);
}

//name放在实例上
class Test {
getInfo(@paramDecorator name: string, age: number) {
console.log(name, age);
}
}

const test = new Test();
test.getInfo('zhou', 1);

4.6应用

通过装饰器将异常捕获的内容封装到了一个函数中,并能复用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const userInfo: any = undefined;

function catchError(msg: string) {
return function(target: any, key: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value;
descriptor.value = function() {
try {
fn();
} catch (error) {
console.log(msg);
}
};
};
}

class Test {
@catchError('userInfo.name不存在')
getName() {
return userInfo.name;
}
@catchError('userInfo.age不存在')
getAge() {
return userInfo.age;
}
}

const test = new Test();
test.getName();
test.getAge();

4.7reflect-metadata

1
2
3
4
5
6
7
8
9
10
import 'reflect-metadata';

const user = {
name: 'zhou'
};

//在user下定义了一组元数据,key为'data' value为'test'
Reflect.defineMetadata('data', 'test', user);
//获取元数据
console.log(Reflect.getMetadata('data', user));

可以在类的属性以及类的方法上定义元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
@Reflect.metadata('data', 'test')
class User {
name = 'zhou';
}

console.log(Reflect.getMetadata('data', User));
---------------------
class User {
@Reflect.metadata('data', 'test')
name = 'zhou';
}

console.log(Reflect.getMetadata('data', User.prototype, 'name'));
1
2
3
4
5
6
7
8
9
10
11
class User {
@Reflect.metadata('data', 'test')
getName() {}
}

class Teacher extends User {}

console.log(Reflect.hasOwnMetadata('data', User.prototype, 'getName'));
console.log(Reflect.hasOwnMetadata('data', Teacher.prototype, 'getName'));
//true false
//hasOwnMetadata判断是不是自己的元数据还是说继承来的

API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// define metadata on an object or property
Reflect.defineMetadata(metadataKey, metadataValue, target);
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);

// check for presence of a metadata key on the prototype chain of an object or property
let result = Reflect.hasMetadata(metadataKey, target);
let result = Reflect.hasMetadata(metadataKey, target, propertyKey);

// check for presence of an own metadata key of an object or property
let result = Reflect.hasOwnMetadata(metadataKey, target);
let result = Reflect.hasOwnMetadata(metadataKey, target, propertyKey);

// get metadata value of a metadata key on the prototype chain of an object or property
let result = Reflect.getMetadata(metadataKey, target);
let result = Reflect.getMetadata(metadataKey, target, propertyKey);

// get metadata value of an own metadata key of an object or property
let result = Reflect.getOwnMetadata(metadataKey, target);
let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey);

// get all metadata keys on the prototype chain of an object or property
let result = Reflect.getMetadataKeys(target);
let result = Reflect.getMetadataKeys(target, propertyKey);

// get all own metadata keys of an object or property
let result = Reflect.getOwnMetadataKeys(target);
let result = Reflect.getOwnMetadataKeys(target, propertyKey);

// delete metadata from an object or property
let result = Reflect.deleteMetadata(metadataKey, target);
let result = Reflect.deleteMetadata(metadataKey, target, propertyKey);
// apply metadata via a decorator to a constructor
@Reflect.metadata(metadataKey, metadataValue)
class C {
// apply metadata via a decorator to a method (property)
@Reflect.metadata(metadataKey, metadataValue)
method() {
}
}

4.8装饰器的执行顺序

  • 当多个装饰器应用在一个声明上时会进行如下步骤的操作:
  1. 由上至下依次对装饰器表达式求值;
  2. 求值的结果会被当作函数,由下至上依次调用.
  • 不同装饰器的执行顺序:属性装饰器 > 方法装饰器 > 参数装饰器 > 类装饰器

自封装的元数据装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import 'reflect-metadata';

function showData(target: typeof User) {
for (let key in target.prototype) {
const data = Reflect.getMetadata('data', target.prototype, key);
console.log(data);
}
}

function setData(dataKey: string, msg: string) {
return function(target: User, key: string) {
Reflect.defineMetadata(dataKey, msg, target, key);
};
}

@showData
class User {
@Reflect.metadata('data', 'name')
getName() {}

@setData('data', 'age')
getAge() {}
}
-------------本文结束感谢您的阅读-------------