console.log("hello world!")
How do @Decorators()
really work?
And what can they be used for? 🤷
import "reflect-metadata";
import { Injectable, createContainer } from 'slim-di';
@Injectable()
class FishingGear {}
@Injectable()
class FishingBoat {}
@Injectable()
class FisherManRoot {
constructor(
private readonly gear: FishingGear,
private readonly boat: FishingBoat
){}
}
function main(){
const container = createContainer(FisherManRoot);
/* Logs true */
console.log(container.get(FishingBoat) === container.get(FishingBoat))
}
main()
Typescript 5.0 just came out!
class MyClass {
@Input()
myProp: string;
}
class MyClass {
@Computed()
myMethod(){}
}
@Component()
class MyComponent {}
class MyComponent {
@AccessorDecorator()
get prop(){}
}
class MyComponent {
prop(@Get("id") id: string){}
}
/* My first method decorator 🎉 */
function log() {
console.log("What's up JS-Meetup? ⚡️🚀")
}
log();
function log(target: any, propertyKey: string, descriptor: any) {
console.log("What's up JS-Meetup? ⚡️🚀");
}
class Company {
@log
calculateSalary(salary: number) {
return salary * 0.2;
}
}
function log(
target: Object,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function (this: any, ...args: any[]) {
console.log(`INFO: Running function ${propertyKey} with input ${args}`);
const res = originalMethod.call(this, ...args);
console.log(`INFO: function ${propertyKey} ran with result ${res}`);
return res;
};
}
class Company {
@log
calculateSalary(salary: number) {
return salary * 0.2;
}
}
const c = new Company();
c.calculateSalary(100);
c.calculateSalary(75);
/* Method Decorator */
function log(
target: Object,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function (this: any, ...args: any[]) {
console.log(`INFO: Running function ${propertyKey} with input ${args}`);
const res = originalMethod.call(this, ...args);
console.log(`INFO: function ${propertyKey} ran with result ${res}`);
return res;
};
}
/* Property Decorator */
function withBonus(target: Object, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
get() {
return this.value * 1.2;
},
set(value) {
this.value = value;
},
});
}
class Company {
@withBonus
public total: number = 0;
@log
calculateSalary(salary: number) {
return salary * 0.2;
}
@log
addExpense(value: number){
this.total += value;
}
}
const c = new Company();
const salary = c.calculateSalary(400);
c.addExpense(salary);
console.log("My total is:", c.total);
Short answer: Never!
Use cases are very rare!
🤷♀️ High abstractions which can create a lot of confusion if abused 🤷
@Freeze
class Company {
constructor(
@Inject("Config.BASE_URL")
private readonly baseUrl: string,
@Optional()
private readonly service: HttpService
){}
@Thing()
prop: string;
@FancyLog()
calculateSalary(
@Validate() value: number,
@Heyo() isBigBoss: boolean
){
/* Code here */
};
/**
...
**/
}
import "reflect-metadata";
function Injectable(): ClassDecorator {
return function (target) {
return target;
};
}
/* Stolen from the Angular project 😏 */
export interface Type<T = unknown> extends Function {
new (...args: any[]): T;
}
export interface DIContainer {
get<T = unknown>(token: Type<T>): T;
}
export const createContainer = <T>(root: Type<T>): DIContainer => {
const container = new Map<Type, any>();
const instantiateDeps = (token: Type): unknown => {
if (container.get(token)) {
return container.get(token);
}
const dependencyMetadata: Type[] =
Reflect.getMetadata("design:paramtypes", token.prototype.constructor) ??
[];
const instance = new token(
...dependencyMetadata.map((it) => instantiateDeps(it))
);
container.set(token, instance);
return instance;
};
instantiateDeps(root);
return {
get(token) {
return container.get(token);
},
};
};
@Injectable()
class FishingGear {}
@Injectable()
class FishingBoat {}
@Injectable()
class FisherManRoot {
constructor(
private readonly gear: FishingGear,
private readonly boat: FishingBoat
) {}
}
function main() {
const container = createContainer(FisherManRoot);
/* Logs true */
console.log(container.get(FishingBoat) === container.get(FishingBoat));
}
main();