Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于类装饰器和元数据的问题 #118

Open
zyhcool opened this issue Oct 11, 2019 · 5 comments
Open

关于类装饰器和元数据的问题 #118

zyhcool opened this issue Oct 11, 2019 · 5 comments

Comments

@zyhcool
Copy link

zyhcool commented Oct 11, 2019

有个疑问请教各位:
这篇文章此处有段代码:

type Constructor<T = any> = new (...args: any[]) => T;

const Injectable = (): ClassDecorator => target => {};

class OtherService {
  a = 1;
}

@Injectable()
class TestService {
  constructor(public readonly otherService: OtherService) {}

  testMethod() {
    console.log(this.otherService.a);
  }
}

const Factory = <T>(target: Constructor<T>): T => {
  // 获取所有注入的服务
  const providers = Reflect.getMetadata('design:paramtypes', target); // [OtherService]
  const args = providers.map((provider: Constructor) => new provider());
  return new target(...args);
};

Factory(TestService).testMethod(); // 1

其中 Injectable 装饰器实质上并没有对 TestService 类进行修改,那为什么这边还要加上这个类装饰器呢?
实验证明,如果不加上的话,Reflect.getMetadata('design:paramtypes', target) 将无效,这是为什么呢?

谢谢解答!

@jkchao
Copy link
Owner

jkchao commented Oct 11, 2019

@zyhcool 可以对比一下加了 @Injectable() 与不加时,TestService 编译后的代码是什么

@zyhcool
Copy link
Author

zyhcool commented Oct 15, 2019

@jkchao
谢谢回复!

我修改了一下代码

// ts源码
import "reflect-metadata";

const Injectable = (): ClassDecorator => target => {};

@Injectable()
class C {
    constructor(s:string){

    }
}

function Factory(constructor){
    console.log(Reflect.getMetadata("design:paramtypes",constructor));
}

Factory(C);

这是加入@Injectable() 后编译的js代码:

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3
            ? target
            : desc === null
                ? desc = Object.getOwnPropertyDescriptor(target, key)
                : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
        r = Reflect.decorate(decorators, target, key, desc);
    } else {
        for (var i = decorators.length - 1; i >= 0; i--) {
            if (d = decorators[i]) {
                r = (
                    c < 3
                        ? d(r)
                        : c > 3
                            ? d(target, key, r)
                            : d(target, key)
                ) || r;
            }
        }
    }
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
exports.__esModule = true;
require("reflect-metadata");
var Injectable = function () { return function (target) { }; };
var C = /** @class */ (function () {
    function C(s) {
    }
    C = __decorate([
        Injectable()
    ], C);
    return C;
}());
function Factory(constructor) {
    console.log(Reflect.getMetadata("design:paramtypes", constructor));
}
Factory(C);

这是去掉@Injectable() 后编译的js代码:

"use strict";
exports.__esModule = true;
require("reflect-metadata");
var Injectable = function () { return function (target) { }; };
var C = /** @class */ (function () {
    function C(s) {
    }
    return C;
}());
function Factory(constructor) {
    console.log(Reflect.getMetadata("design:paramtypes", constructor));
}
Factory(C);

本质上加了@Injectable()后,只是多了一个__decorate函数和其中的Reflect.decorate方法,这个是reflect-metadata写的的polyfill,有什么影响吗?
而且运行编译后的js文件(加了@Injectable()的),打印结果却是undefined,为什么?
难道Reflect.metadata只能在TS中使用,而且一定要在作为装饰器的函数中?

@jkchao
Copy link
Owner

jkchao commented Oct 15, 2019

@zyhcool 使用 Reflect Metadata 需要打开 emitDecoratorMetadata 编译选项,然后再对比一下编译后的结果,其中使用 @ Injectable 的,有一句很关键的代码 __metadata("design:paramtypes", [String]),把 [String] 存储到了 key 为 design:paramtypes 的元信息里面。

@zyhcool
Copy link
Author

zyhcool commented Oct 17, 2019

@jkchao 谢谢大佬,已经解决我的疑惑了!
我之前使用tsc编译确实没有带上--experimentalDecorators--emitDecoratorMetadata选项,加上后得到的编译结果多了个__metadata函数

var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};

@sultan-young
Copy link

牛!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants