is not Injectable decorator required in providers? - nestjs

I'm studying NestJS with a sample app.
https://github.com/nestjs/nest/tree/master/sample/12-graphql-schema-first
However, what I am curious about is that even if the service does not have an injectable decorator, it can be registered as a provider of the module, and the constructor of other providers can use the registered provider without the injectable decorator.
Actually, I removed the injectable decorator from src/cats/cats.service.ts in the example above. But it works fine.
Even without the Injectable decorator, the same object was passed to the provider's constructor.
Why is the injectable decorator necessary?

injectable is to allow injecting providers in that class. If you don't have anything to inject, you don't need that decorator. But I'd suggest you to always use it.
For detailed info on this topic, read this: https://github.com/nestjs/docs.nestjs.com/pull/2481

The answer here is: "it depends".
If a provider has no injected dependencies, then technically, no you don't need the #Injectable() decorator. What that decorator is doing under the hood is forcing Typescript to emit constructor parameter metadata. Nest will read that metadata at runtime to know what is injected into the provider. This PR goes into more depth on the #Injectable() decorator itself
If we have the following classes
#Injectable()
export class Foo {
constructor(private readonly foo: string) {}
}
#Injectable()
export class Bar {
constructor(private readonly bar: string) {}
}
#Injectable()
export class FooBar {
constructor(private readonly foo: Foo, private readonly bar: Bar) {}
}
Then we get a compiled output like this
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);
};
let Foo = class Foo {
constructor(foo) {
this.foo = foo;
}
};
Foo = __decorate([
Injectable(),
__metadata("design:paramtypes", [String])
], Foo);
export { Foo };
let Bar = class Bar {
constructor(bar) {
this.bar = bar;
}
};
Bar = __decorate([
Injectable(),
__metadata("design:paramtypes", [String])
], Bar);
export { Bar };
let FooBar = class FooBar {
constructor(foo, bar) {
this.foo = foo;
this.bar = bar;
}
};
FooBar = __decorate([
Injectable(),
__metadata("design:paramtypes", [Foo, Bar])
], FooBar);
export { FooBar };
The __metadata('design:paramtypes', []) is what Nest eventually reads, and matches to the DI container injection tokens

Related

Accessing inline type in the constructor

I have this:
export class QueueEntity<T> implements HasInternalQueue<T> {
opts: { // <--- inline type here
foo: boolean
}
constructor(v: typeof this.opts) { // this doesn't quite work
this.opts = v
}
}
is there a way to reference the inline type or is this not possible?
Since there is no way to define a type inside the class directly (An open issue in the TS repo). I think you can use the class name to reference this:
export class QueueEntity<T> {
opts: { // <--- inline type here
foo: boolean
}
constructor(v: QueueEntity<T>['opts']) { // <-- should work now
this.opts = v
}
}
const obj = new QueueEntity({ foo : false });
const obj2 = new QueueEntity({ foo2 : false });
const obj3 = new QueueEntity();
Playground

how to memoize a TypeScript getter

I am using the following approach to memoize a TypeScript getter using a decorator but wanted to know if there is a better way. I am using the popular memoizee package from npm as follows:
import { memoize } from '#app/decorators/memoize'
export class MyComponent {
#memoize()
private static memoizeEyeSrc(clickCount, maxEyeClickCount, botEyesDir) {
return clickCount < maxEyeClickCount ? botEyesDir + '/bot-eye-tiny.png' : botEyesDir + '/bot-eye-black-tiny.png'
}
get leftEyeSrc() {
return MyComponent.memoizeEyeSrc(this.eyes.left.clickCount, this.maxEyeClickCount, this.botEyesDir)
}
}
AND the memoize decorator is:
// decorated method must be pure
import * as memoizee from 'memoizee'
export const memoize = (): MethodDecorator => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
const func = descriptor.value
descriptor.value = memoizee(func)
return descriptor
}
}
Is there a way to do this without using two separate functions in MyComponent and to add the decorator directly to the TypeScript getter instead?
One consideration here is that the decorated function must be pure (in this scenario) but feel free to ignore that if you have an answer that doesn't satisfy this as I have a general interest in how to approach this problem.
The decorator can be extended to support both prototype methods and getters:
export const memoize = (): MethodDecorator => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
if ('value' in descriptor) {
const func = descriptor.value;
descriptor.value = memoizee(func);
} else if ('get' in descriptor) {
const func = descriptor.get;
descriptor.get = memoizee(func);
}
return descriptor;
}
}
And be used directly on a getter:
#memoize()
get leftEyeSrc() {
...
}
Based on #estus answer, this is what I finally came up with:
#memoize(['this.eyes.left.clickCount'])
get leftEyeSrc() {
return this.eyes.left.clickCount < this.maxEyeClickCount ? this.botEyesDir + '/bot-eye-tiny.png' : this.botEyesDir + '/bot-eye-black-tiny.png'
}
And the memoize decorator is:
// decorated method must be pure when not applied to a getter
import { get } from 'lodash'
import * as memoizee from 'memoizee'
// noinspection JSUnusedGlobalSymbols
const options = {
normalizer(args) {
return args[0]
}
}
const memoizedFuncs = {}
export const memoize = (props: string[] = []): MethodDecorator => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
props = props.map(prop => prop.replace(/^this\./, ''))
if ('value' in descriptor) {
const valueFunc = descriptor.value
descriptor.value = memoizee(valueFunc)
} else if ('get' in descriptor) {
const getFunc = descriptor.get
// args is used here solely for determining the memoize cache - see the options object
memoizedFuncs[propertyKey] = memoizee((args: string[], that) => {
const func = getFunc.bind(that)
return func()
}, options)
descriptor.get = function() {
const args: string[] = props.map(prop => get(this, prop))
return memoizedFuncs[propertyKey](args, this)
}
}
return descriptor
}
}
This allows for an array of strings to be passed in which determine which properties will be used for the memoize cache (in this case only 1 prop - clickCount - is variable, the other 2 are constant).
The memoizee options state that only the first array arg to memoizee((args: string[], that) => {...}) is to be used for memoization purposes.
Still trying to get my head around how beautiful this code is! Must have been having a good day. Thanks to Yeshua my friend and Saviour :)

this is undefined in extended class

I have these 3 files:
bar.js
class Bar {
test(p) {
console.log(p);
}
}
module.exports = Bar;
baz.js
const Bar = require('./bar');
class Baz extends Bar {
test2(p) {
this.test(p);
}
}
module.exports = Baz;
foo.js
const Baz = require('./baz');
const { test2 } = new Baz();
test2('test');
When I passed 'test' to new Baz.test2(), I expected it to pass it to it's superclass (this.test(p)) where it should log 'test'. However, it threw the error:
this.test(p);
^
TypeError: Cannot read property 'test' of undefined
What am I doing wrong? Why is this undefined, I thought it should reference the class itself?
test2 is used apart from it's original context (Bar instance) and should be bound to proper this.
It's impossible to say from the name if it's expected to be a callback by design. In case it is, it can be bound in constructor:
class Bar {
constructor () {
this.test = this.test.bind(this);
}
...
}
Otherwise it can be bound in-place:
const test2 = new Baz().test2.bind(this);
test2('test');
Or just not be used separately from the context:
const baz = new Baz()
baz.test2('test');

Is it possible to extend a class method in JavaScript?

Suppose I have a class A with the following method:
foo(x, options) {
const limit = options && options.limit ? options.limit : undefined;
const select = options && options.select ? options.select : {};
const paginate = options && options.paginate ? options.paginate : {};
const sort = options && options.sort ? options.sort : {};
const count = options && options.count;
const args = deepMerge({ x }, paginate);
return count ? this.model.find(args).count()
: this.model.find(args).limit(limit).select(select).sort(sort);
}
Then I create a class B that extends A. There I have a method bar that is almost identical to method foo.
Is it possible to extend method bar to have limit,select,paginate,sort and count? I don't want to override foo method in B class, so I need it to have a distinct name
You can use the this.foo() if you haven't overwritten the foo() method on B, else, you can use the super keyword which refers to the parent class instance super.foo() and it works in both cases (overwritten or not).
class B extends A {
bar(){
super.foo();
}
}
Disclaimer: I'm assuming that: by class, you mean ES6 classes not ES5 prior prototypes.
Working example:
class A {
foo() {
return 'value of A';
}
}
class B extends A {
// case you override the method
// super.foo() still points to the
// original A.foo() but you don't
// have to do this, it's just an
// example
foo() {
return 'value of B';
}
bar() {
const valueOfA = super.foo();
return valueOfA;
}
}
const b = new B();
console.log(b.bar())

Coercion befuddlement in Groovy

Why does the following
class Test {
#Test
void go() {
def foo1 = new MockFoo1() as Foo
def foo2 = new MockFoo2() as Foo
}
interface Foo {}
class MockFoo1 {}
class MockFoo2 {}
}
Result in a java.lang.IllegalArgumentException: argument type mismatch on the foo2 coercion?
This only happens if I coerce 2 objects of 2 different types to the same interface during a single path of execution. The groovy approved way of using closures or maps to achieve this kind of duck typing works fine.
Any light shed appreciated.
It's a bug with the ProxyGenerator adapterCache. As a workaround, you can also use some Groovy trickery to make this work:
interface Foo {
static a = {
[MockFoo1, MockFoo2].each {
it.metaClass.asType = { Class klazz ->
try {
DefaultGroovyMethods.asType(delegate, klazz)
} catch (e) {
def cache = ProxyGenerator.INSTANCE.#adapterCache.#cache
cache.each { k, v ->
cache.remove(k)
}
DefaultGroovyMethods.asType(delegate, klazz)
}
}
}
}()
}
class MockFoo1 {}
class MockFoo2 {}
def a = new MockFoo1() as Foo
def b = new MockFoo2() as Foo
assert a instanceof Foo
assert b instanceof Foo
Hope this helps!

Resources