Externalize strings and use a formatter to substitute variables into string - string

I'd like to externalize some strings but still use some form of string substitution.
In some of my node based projects I've used:
var format = require('string-format');
format(Constants.COUNTRY_WEATHER_ENDPOINT, {
country: country
})
However In Typescript, I've been trying something like this ..
Error:
Cannot find name 'country'.at line 18 col 66 in repo/src/app/constants.ts
Constants.ts
export class Constants {
public static COUNTRY_WEATHER_ENDPOINT = `/api/country/${country}/weather`;
}
TestService.ts
import { Constants } from './constants';
export class TestService {
constructor(private $http) { }
public getWeather(country) {
let url = Constants.COUNTRY_WEATHER_ENDPOINT;
return this.$http.get(
url,
.then(response => response);
}
}
TestService.$inject = ['$http'];

Use arrow functions:
export const Constants: { readonly [name: string]: (key: string) => string } = {
countryWeatherEndpoint: country => `/api/country/${country}/weather`
}
Then do:
import { Constants } from "./constants";
// ...
const url = Constants.countryWeatherEndpoint(country);
// use your url

String interpolation in Typescript needs your variable country to exist when class is loaded. If you want to format later the constant string, you have to use normal quotes (single or double) and call format in your service.

Related

TypeScript: Elegant way to write single line helper functions

I have a simple helper / util sort of class where there are some one/two liner utilities which I am planning to use across the code.
I am using Nest Js/Node Js with TypeScript.
I am just thinking how better, elegantly I can write these helper methods, preferably in a one-liner sort of .. sweet and nice.
My attempt so far
import { Injectable } from '#nestjs/common';
#Injectable()
export class HelperOperations {
static zones(region: string): string[] {
return region.split(',', 2);
}
static instanceNames(name: string): string[] {
return [`${name}-vm1`, `${name}-vm2`];
}
static mediatorName(name): string {
return `${name}-mediator`;
}
static mediatorZone(region: string): string {
return region.split(",").pop();
}
static zoneNamePair(name, region): [string, string][] {
const zoneNamePair: [string, string][] = [];
const zones = HelperOperations.zones(region);
HelperOperations.instanceNames(name).map((instName, i) => zoneNamePair.push([instName, zones[i]]));
return zoneNamePair;
}
}
Expected Outcome:
Better, elegant way to rewrite this class for betterment, of course strongly typed.
Generally (dunno about Nest) this is done with exports or with namespaces
export const zones = (region: string) => region.split(',', 2);
export namespace z1 {
export const zones = (region: string) => region.split(',', 2);
}
////////////
import {zones} from './z'
import * az z0 from './z'
import {z1} from './z'
zones('a')
z0.zones('a')
z1.zones('a')

Get filename of derived class from base class in typescript running on node.js?

I'm looking for a way to get the filename of a derived class from a base class in typescript running on node.js. An example of this would be:
Foo.ts
export abstract class Foo {
constructor() { }
name() { return (__filename); }
print() { console.log(this.name()); }
}
Bar.ts
import { Foo } from './Foo';
export class Bar extends Foo {
constructor() { super(); }
}
main.ts
import { Bar } from './Bar';
let bar = new Bar();
bar.print(); // should yield the location of Bar.ts
Due to the number of files involved and just cleanliness I'd like this to be confined to the Foo class rather than having an override of the name() function in each derived class.
I was able to sort-of solve this with the code:
private getDerivedFilePath(): string {
let errorStack: string[] = new Error().stack.split('\n');
let ret: string = __filename;
let baseClass: any = ThreadPoolThreadBase;
for (let i: number = 3; i < errorStack.length; i++) {
let filename: string = errorStack[i].slice(
errorStack[i].lastIndexOf('(') + 1,
Math.max(errorStack[i].lastIndexOf('.js'), errorStack[i].lastIndexOf('.ts')) + 3
);
let other: any = require(filename);
if (other.__proto__ === baseClass) {
ret = filename;
baseClass = other;
} else {
break;
}
}
return (ret || '');
}
Added to Foo, which will work when called from the constructor to set a private _filename property, for inheritance chains beyond the example above so long as the files are structured with a default export of the class being used. There may also be a caveat that if a base class from which a derived object is inheriting directly is initialized as a separate instance within the constructor of any member of the inheritance chain it could get confused and jump to another independent derived class - so it's a bit of a hacky work-around and I'd be interested if someone comes up with something better, but wanted to post this in case someone stumbles across this question and it works for them.
You can use require.cache to get all cached NodeModule objects and filter it to find your module.
https://nodejs.org/api/modules.html#requirecache
class ClassA {
public static getFilePath():string{
const nodeModule = this.getNodeModule();
return (nodeModule) ? nodeModule.filename : "";
}
public static getNodeModule(): NodeModule | undefined{
const nodeModule = Object.values(require.cache)
.filter((chl) => chl?.children.includes(module))
.filter((mn)=> mn?.filename.includes(this.name))
.shift();
return nodeModule;
}
}
class ClassB extends ClassA {
constructor(){}
}
const pathA = ClassA.getFilePath(); //Must return the absolute path of ClassA
const pathB = ClassB.getFilePath(); //Must return the absolute path of ClassB

Reflect metadata design:paramtypes return an array with unfedined element

I'm trying to obtain the constructor arguments for some class but the <> get an array with a undefined element.
For the test I work with services Example2Services and ExampleService
Example2Service.ts
import { ExampleService } from './example.service';
export class Example2Service {
constructor(private es1: ExampleService) {}
getValue() {
return "some value2";
}
}
ExampleService.ts
import { Example2Service } from './example2.service';
export class ExampleService {
constructor(private es2: Example2Service) { }
getValue() {
return this.es2.getValue();
}
getString() {
return "1"
}
}
In this function I send a Example2Service
private resolve(target: Type): Object | Function {
let tokens = Reflect.getMetadata('design:paramtypes', target)
...
}
debugging the code, I get this for the token variable
trying to understand the behavior, I try to obtain the args directly from some classes
import {Example2Service} from '../../exaple2.service.ts';
import {ExampleService} from '../../exaple.service.ts';
private resolve(target: Type): Object | Function {
let tokensA = Reflect.getMetadata('design:paramtypes', Example2Service)
let tokensB = Reflect.getMetadata('design:paramtypes', ExampleService);
let tokens = Reflect.getMetadata('design:paramtypes', target);
...
}
debuging this case I get this, sending a Example2Service to resolve function. Now tokens is an array with an Class inside. Note that when importing the Example2Service and ExampleService this happened, but when changing the import order the behavior changes
EDIT
I found out that this occurs when there is circularity between dependencies

tsoa #Route decorator with variable dosen't works in express app

I can't find any information wh #Route decorator doesn't work with a variable instead of "string".
A swagger definition generated with code below is wrong.
export const PATH_GROUP = "/my-path";
export enum PATHS {
GetButtons = "/",
}
#Route(PATH_GROUP)
export class ButtonsController {
#Get(PATH_GROUP.GetButtons)
#Tags("Buttons")
public static async getButtons(): Promise<ButtonViewModel[]> {
const buttons = await buttonsService.getAllButtons();
return buttons.map(button => button.toAddToViewModel());
}
}
When I generate a new swagger definition it looks like:
If I use strings instead of variable/enum everything works correctly.
#Route("/my-path")
export class ButtonsController {
#Get("/")
#Tags("Buttons")
public static async getButtons(): Promise<ButtonViewModel[]> {
const buttons = await buttonsService.getAllButtons();
return buttons.map(button => button.toAddToViewModel());
}
}
Is there any information about not using variables in tsoa decorators?

Why and when should I use TypeScript's "return X as Y"?

Reading a code sample that contains the code below.
import { Group } from '../models/Group'
export class GroupAccess {
constructor(
private readonly docClient: DocumentClient = createDynamoDBClient(),
private readonly groupsTable = process.env.GROUPS_TABLE) {
}
async getAllGroups(): Promise<Group[]> {
console.log('Getting all groups')
const result = await this.docClient.scan({
TableName: this.groupsTable
}).promise()
const items = result.Items
return items as Group[]
}
...
content in the ../models/Group
export interface Group {
id: string
name: string
description: string
userId: string
timestamp: string
}
Q:
items is an AWS.DynamoDB.DocumentList.ItemList. Is the code below trying to typecast to Group[]? The syntax looks different from the regular type conversion https://www.w3schools.com/js/js_type_conversion.asp
return items as Group[]

Resources