Home > front end >  How does NestJS match the injection target and the injected instance?
How does NestJS match the injection target and the injected instance?

Time:06-27

According to the NestJS official document [1], it describes "In Nest, thanks to TypeScript capabilities, it's extremely easy to manage dependencies because they are resolved just by type."

I'm very confused of this description, because at runtime we do not have any type information of the constructor argument which is erased during the transpilation.

How NestJS actually resolves the matching between the required type (class) and the actual object (instance)?

CodePudding user response:

Thanks to decorators, we actually have access to some types, through Typescript emitted metadata. It's with this metadata that Nest is able to read to know how to inject a class instance into a provider. This is the compiled JS from an app.service.ts to app.service.js file from nest new

"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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppService = void 0;
const common_1 = require("@nestjs/common");
let AppService = class AppService {
    getHello() {
        return 'Hello World!';
    }
};
AppService = __decorate([
    (0, common_1.Injectable)()
], AppService);
exports.AppService = AppService;
//# sourceMappingURL=app.service.js.map

And this is the app.controller.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;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppController = void 0;
const common_1 = require("@nestjs/common");
const app_service_1 = require("./app.service");
let AppController = class AppController {
    constructor(appService) {
        this.appService = appService;
    }
    getHello() {
        return this.appService.getHello();
    }
};
__decorate([
    (0, common_1.Get)(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", String)
], AppController.prototype, "getHello", null);
AppController = __decorate([
    (0, common_1.Controller)(),
    __metadata("design:paramtypes", [app_service_1.AppService])
], AppController);
exports.AppController = AppController;
//# sourceMappingURL=app.controller.js.map

If we look at the last few lines of app.controller.js we can see a line __metadata("design:paramtypes", [app_service_1.AppService]). This line means that the constructor of AppController has parameters of type AppService that should be passed to it. Nest is able to read this, check if there is an AppService in the module's container's providers, and if so pass the instance that is in the providers map to the new AppController(). This emitted type data is available for classes that have a decorator on them, and the emitted type value is always a class, whether it is the custom class used or if it is a primitive class like Object or String, though in the latter cases, there should be a custom parameter metadata being emitted via the @Inject() decorator.

CodePudding user response:

Actually, there is nothing magical in there. Feel free to look https://github.com/nestjs/nest/blob/874344c60efddba0d8491f8bc6da0cd45f8ebdf7/packages/core/scanner.ts#L415

  • Related