ts 装饰器不支持函数, 只能在类上面加, 有点难受....
https://www.tslang.cn/docs/handbook/decorators.html#decorator-factories
由于tsdx只是一个库打包工具, 不会开启server所以我们需要使用parcel做一层转发
或者使用storybook
npx tsdx ioc-demo
或者
npx tsdx create ioc-demo
npm install inversify reflect-metadata parcel-bundler --save
yarn add inversify reflect-metadata parcel-bundler
yarn start
yarn dev
tsconfig
{
"compilerOptions": {
"target": "es5",
"lib": ["es6"],
"types": ["reflect-metadata"],
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
例子
可以看到确实已经拓展了方法, 加了run函数, 但是ts的类型提示还是有问题, 得好好研究下
import _debounce from 'lodash-es/debounce';
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log('log:', target, propertyKey, descriptor);
}
function debounce(wait: number = 100) {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log('debounce:', wait, target, propertyKey, descriptor);
descriptor.value = _debounce(descriptor.value, wait);
};
}
interface IRun {
hello:string;
run():void;
}
function addRun<T extends {new(...args:any[]):{}}>(constructor: T) {
return class extends constructor implements IRun {
// newProperty = "new property";
hello = 'override';
run() {
console.log('run', this);
}
};
}
@addRun
class Stu {
name: string = '';
constructor(name: string = '') {
this.name = name;
}
@log
play() {
console.log('play: ', this.name);
}
@debounce(100)
study() {
console.log('study2: ', this.name);
}
}
const s = new Stu('ace');
for (let i = 0; i < 3; i++) s.play();
for (let i = 0; i < 3; i++) s.study();
console.log('s', s);
// s.run()
数组参数必须传入
先执行参数装饰器, 再执行方法装饰器, 在执行参数装饰器时, 加上一个该方法需要必传参数的索引数组, 然后在方法装饰器中对传入方法的数组做校验
import 'reflect-metadata';
const requiredMetadataKey = Symbol('required');
function required(
target: Object,
propertyKey: string | symbol,
parameterIndex: number
) {
console.log('required');
let existingRequiredParameters: number[] =
Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata(
requiredMetadataKey,
existingRequiredParameters,
target,
propertyKey
);
}
function validate(target: any, propertyName: string, descriptor: any) {
console.log('validate');
let method = descriptor.value;
descriptor.value = function(...args: any[]) {
let requiredParameters: number[] = Reflect.getOwnMetadata(
requiredMetadataKey,
target,
propertyName
);
if (requiredParameters) {
for (let parameterIndex of requiredParameters) {
if (
parameterIndex >= args.length ||
args[parameterIndex] === undefined
) {
console.error('Missing required argument.');
}
}
}
return method.apply(this, args);
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@validate
greet(@required name: string) {
return 'Hello ' + name + ', ' + this.greeting;
}
}
const g = new Greeter('ace');
console.log(g.greet('abc'));
g.greet();
ts中的接口和类
interface I {
I(): void;
}
class A {
A() {
console.log('A');
}
}
class B extends A implements I {
B() {
console.log('B');
}
I() {
console.log('I');
}
}
const b = new B();
b.A();
b.B();
b.I();
修改属性访问器
// 注意 TypeScript不允许同时装饰一个成员的get和set访问器。取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个属性描述符时,它联合了get和set访问器,而不是分开声明的。
// 访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
// 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
// 成员的名字。
// 成员的属性描述符。
// 注意 如果代码输出目标版本小于ES5,Property Descriptor将会是undefined。
// 如果访问器装饰器返回一个值,它会被用作方法的属性描述符。
// 注意 如果代码输出目标版本小于ES5返回值会被忽略。
function configurable(value: boolean) {
return function(descriptor: PropertyDescriptor) {
descriptor.configurable = value;
descriptor.writable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() {
return this._x;
}
@configurable(false)
get y() {
return this._y;
}
}
const p = new Point(1, 1);
console.log(p.x);
console.log(p.y);
p.x = 2 // 会报错, 不能设置新值
属性格式化
import 'reflect-metadata';
const formatMetadataKey = Symbol('format');
function format(formatString: string) {
return Reflect.metadata(formatMetadataKey, formatString);
}
function getFormat(target: any, propertyKey: string) {
return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}
class Greeter {
@format('Hello, %s')
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
let formatString = getFormat(this, 'greeting');
return formatString.replace('%s', this.greeting);
}
}
const g = new Greeter('ace');
console.log(g.greet());
来源:oschina
链接:https://my.oschina.net/ahaoboy/blog/4524541