I am trying to import class and use its public methods but its not working , what is correct way implement it.
main.ts
import {PromiseHandler } from './promiseHandler.ts';
export function getUser(req: Request, res: Response) {
const promiseHandler: new PromiseHandler();
}
promiseHandler.ts
export class PromiseHandler {
constructor() {};
public executeAllPromises(promises) {
}
As told in comments, the correct syntax is
const promiseHandler = new PromiseHandler();
(note the use of =, to assign the object created, while : would be to type the variable. The type is actually just PromiseHandler, so you could use both and write :
const promiseHandler: PromiseHandler = new PromiseHandler();
but I think that is not necessary to declare type here, TypeScript would detect it the correct type by itself with the initialisation with = new...
Related
First of all the question title doesn't make sense. Im not sure how can I ask the question in a better way.
Im facing an issue in an Angular 8.1 project. There is a josn file Im importing that in the settings Class (its a service).
When the environment.ts file have a variable as app_company. the same name a json file also available. So if the client app_company matches the json file. all the config should be loaded from that file. This is what I need to achieve
What I tried so far is .
import { Injectable } from '#angular/core';
import { environment } from '#env/environment';
import * as client1 from './client1.json';
#Injectable({
providedIn: 'root'
})
export class SettingsService {
newInstance:object;
constructor() { }
config(key : string){
// const newInstance = Object.create(window[environment.app_company].prototype);
// newInstance.constructor.apply(newInstance);
// return newInstance.key;
// const newInstance: any = new (<any>window)[environment.app_company];
// console.log(Object.create(window[environment.app_company]));
return environment.app_company.key;
}
}
my json file will look like below
{
"opNumberLabel" : "OP No"
}
So in the return section if I call client1.opNumberLabel
It works like my expectation , but I try to make that dynamic like environment.app_company.key it not working .
As you can see my tried are commented those are not worked so far :(.
Any hint will highly appreciated .
Thanks in advance,
Finally I resolved.
export class SettingsService {
clients = { client1 , client2 };
app_company :string;
default_client = 'client1';
constructor() { }
config(label : string){
this.app_company = environment.app_company;
if(this.clients[this.app_company].default.hasOwnProperty(label)){
return this.clients[this.app_company].default[label];
}else{
return this.clients[this.default_client].default[label];
}
}
}
Thanks to #Titus for the hint.
Would you like to achieve something like that?
import { Injectable } from '#angular/core';
import * as settings from "./environment.json";
interface Settings {
[key: string]: string
}
type Environment = {
[key: string]: Partial<Settings>
};
#Injectable({
providedIn: 'root'
})
export class SettingsService {
private environment: Environment = {
app_company: { }
};
constructor() {
this.environment.app_company = settings;
console.log(this.config("opNumberLabel")); // OP No
}
config(key : string){
return this.environment.app_company[key];
}
}
{
"opNumberLabel" : "OP No",
"settings1": "testSettings1",
"settings2": "testSettings2"
}
If you would like to have app_company as a dynamic value too, then I would suggest to extend the example:
by creating an init function which could also accept the name of
the client.
by passing an object to the SettingsService
constructor, with InjectionToken, so you can define what is the
current client you are trying to configure.
You might also want to have different environment setup for different clients, so I would suggest to create separate environment json files for each client.
You could create an object which would hold every <client>.environment.json with <client> key.
After that you have an object, like that:
const clientEnvironments: Environment = {
client1: { ... },
client2: { ... }
};
So by having the name of your current client, you can easily select which environment you want to use.
I can't understand why the next issue happens. Code with the same structure works fine in any other object-oriented languages like C# or Java.
I am writing Node.Js app using Typescript.
So I have a class ServiceLocator with two static variables:
//ServiceLocator.ts
export class ServiceLocator {
public static dataRepository: IDataRepository = new DataRepository();
public static authService: IAuthService = new AuthService();
}
Class from the second one variable using the first one static variable. This how it looks:
//AuthService.ts
const dataRepository: IDataRepository = ServiceLocator.dataRepository;
export class AuthService implements IAuthService {
...
}
But once I am trying to get authService link like this:
//AuthController.ts
const authService: IAuthService = ServiceLocator.authService;
export class AuthController {
public async signIn(request: Request, response: Response) {
..
}
..
}
I got an error:
TypeError: Cannot read property 'dataRepository' of undefined
What am I do wrong?
You have a circular reference here. In order to construct the AuthService module you need a fully constructed ServiceLocator class. However, JavaScript needs a fully constructed AuthService module in order to construct the ServiceLocator class. Switching the order of instantiation would simply result in an error from ServiceModule <cint> (to coin a Java-ism) along the lines of "Uncaught TypeError: undefined is not a constructor".
The solution is to simply make the dependency lazy:
//AuthService.ts
const dataRepositoryReference: IDataRepository = import('./ServiceLocator')
.then(({ ServiceLocator }) => ServiceLocator.dataRepository);
export class AuthService implements IAuthService {
public async findUserByUsername(username: UserName) {
const dataRepository = await dataRepositoryReference;
// ... snip ...
}
}
Consider the following code:
import redis = require('redis'); //Has ambient declaration from DT
import bluebird = require('bluebird'); //Has ambient declaration from DT
bluebird.promisifyAll((<any>redis).RedisClient.prototype);
bluebird.promisifyAll((<any>redis).Multi.prototype);
const client = redis.createClient();
client.getAsync('foo').then(function(res) {
console.log(res);
});
getAsync will error out because it's created on the fly and not defined in any .d.ts file. So what is the proper way to handle this?
Also, even though I have the .d.ts files loaded for redis, I still need to cast redis to any to be used for promisifyAll. Otherwise, it will spill out error:
Property 'RedisClient' does not exist on type 'typeof "redis"'
Is typing it to any the only easy way to go?
I'm solving this by declaration merging the setAsync & getAsync methods. I added the following code in my own custom .d.ts file.
declare module "redis" {
export interface RedisClient extends NodeJS.EventEmitter {
setAsync(key:string, value:string): Promise<void>;
getAsync(key:string): Promise<string>;
}
}
Another way to do it which requires less code is to extend the Redis object like so:
import { promisify } from 'util';
import { ClientOpts, RedisClient } from 'redis';
class AsyncRedis extends RedisClient {
public readonly getAsync = promisify(this.get).bind(this);
public readonly setAsync = promisify(this.set).bind(this);
public readonly quitAsync = promisify(this.quit).bind(this);
public readonly rpushAsync: (list: string, item: string) => Promise<number> = promisify(
this.rpush
).bind(this);
public readonly blpopAsync: (
list: string,
timeout: number
) => Promise<[string, string]> = promisify(this.blpop).bind(this);
public readonly flushdbAsync = promisify(this.flushdb).bind(this);
}
Notice that not all method signatures overwrite correctly, so you have to help typescript a little.
Now you can just use this enhanced class by creating it with your options, for example:
new AsyncRedis({
host: process.env.REDIS_HOST || '127.0.0.1',
password: process.env.REDIS_PASSWORD || 'whatever',
});
Just adding to Dave's answer, in my needs, I has to add in Multi for atomic operations.
declare module 'redis' {
export interface RedisClient extends NodeJS.EventEmitter {
execAsync(...args: any[]): Promise<any>;
hgetallAsync(...args: any[]): Promise<any>;
// add other methods here
}
export interface Multi extends Commands<Multi> {
execAsync(...args: any[]): Promise<any>;
// add other methods here
}
}
This solution works fine for me:
import { promisifyAll } from 'bluebird'; // import here works only if #types/bluebird is installed
import redis, { RedisClient, Multi } from 'redis'; // import here works only if #types/redis is installed
// Convert Redis client API to use promises, to make it usable with async/await syntax
const MultiAsync: any = promisifyAll(Multi.prototype);
const RedisClientAsync: any = promisifyAll(RedisClient.prototype);
const redisAsync = { ...redis, Multi: MultiAsync, RedisClient: RedisClientAsync };
const client: typeof RedisClientAsync = redisAsync.createClient();
// now you can use client async methods, i.e. client.getAsync, client.hgetAsync, client.hsetAsync, client.expireAsync...
I'm building a flow-type definition for Sequelize and have run into a small problem with the declare section of the flow-type.
Specifically Sequelize generally is defined as:
class Sequelize {
....
}
Sequelize.DataTypes = DataTypes;
I can generate a flow-type declaration for either one but not both simultaneously. When I put DataTypes in a class they're member variables not available to the instantiated scope.
declare export default class sequelize$Class {
constructor(...);
DataTypes: sequelize$DataTypes;
}
declare var DataTypes: sequelize$DataTypes;
declare export var DataTypes;
Since I need to write code that looks like:
const db = new Sequelize(...)
const MyModel = db.define(... { email: { type: Sequelize.DataTypes.STRING } })
For the moment I've ended up putting this in place:
import Sequelize, { DataTypes } from "sequelize";
const sequelize: sequelize$Sequelize = ((new Sequelize(settings.database, { logging: false })): any);
There has to be a better way.
I'm going to give this a shot and let me know if it helps you out :)
You can declare the sequelize definition like so
declare module 'sequelize' {
declare type sequelize$DataTypes = {
STRING: string
}
declare export default class sequelize$Class {
static DataTypes: sequelize$DataTypes;
constructor(database: Object, options: Object): void;
}
declare export var DataTypes: sequelize$DataTypes;
}
This gives you a default export as well as a named export. I pre-define sequelize$DataTypes, pass it into sequelize$Class as a static property, and export it.
Consider the following code:
import redis = require('redis'); //Has ambient declaration from DT
import bluebird = require('bluebird'); //Has ambient declaration from DT
bluebird.promisifyAll((<any>redis).RedisClient.prototype);
bluebird.promisifyAll((<any>redis).Multi.prototype);
const client = redis.createClient();
client.getAsync('foo').then(function(res) {
console.log(res);
});
getAsync will error out because it's created on the fly and not defined in any .d.ts file. So what is the proper way to handle this?
Also, even though I have the .d.ts files loaded for redis, I still need to cast redis to any to be used for promisifyAll. Otherwise, it will spill out error:
Property 'RedisClient' does not exist on type 'typeof "redis"'
Is typing it to any the only easy way to go?
I'm solving this by declaration merging the setAsync & getAsync methods. I added the following code in my own custom .d.ts file.
declare module "redis" {
export interface RedisClient extends NodeJS.EventEmitter {
setAsync(key:string, value:string): Promise<void>;
getAsync(key:string): Promise<string>;
}
}
Another way to do it which requires less code is to extend the Redis object like so:
import { promisify } from 'util';
import { ClientOpts, RedisClient } from 'redis';
class AsyncRedis extends RedisClient {
public readonly getAsync = promisify(this.get).bind(this);
public readonly setAsync = promisify(this.set).bind(this);
public readonly quitAsync = promisify(this.quit).bind(this);
public readonly rpushAsync: (list: string, item: string) => Promise<number> = promisify(
this.rpush
).bind(this);
public readonly blpopAsync: (
list: string,
timeout: number
) => Promise<[string, string]> = promisify(this.blpop).bind(this);
public readonly flushdbAsync = promisify(this.flushdb).bind(this);
}
Notice that not all method signatures overwrite correctly, so you have to help typescript a little.
Now you can just use this enhanced class by creating it with your options, for example:
new AsyncRedis({
host: process.env.REDIS_HOST || '127.0.0.1',
password: process.env.REDIS_PASSWORD || 'whatever',
});
Just adding to Dave's answer, in my needs, I has to add in Multi for atomic operations.
declare module 'redis' {
export interface RedisClient extends NodeJS.EventEmitter {
execAsync(...args: any[]): Promise<any>;
hgetallAsync(...args: any[]): Promise<any>;
// add other methods here
}
export interface Multi extends Commands<Multi> {
execAsync(...args: any[]): Promise<any>;
// add other methods here
}
}
This solution works fine for me:
import { promisifyAll } from 'bluebird'; // import here works only if #types/bluebird is installed
import redis, { RedisClient, Multi } from 'redis'; // import here works only if #types/redis is installed
// Convert Redis client API to use promises, to make it usable with async/await syntax
const MultiAsync: any = promisifyAll(Multi.prototype);
const RedisClientAsync: any = promisifyAll(RedisClient.prototype);
const redisAsync = { ...redis, Multi: MultiAsync, RedisClient: RedisClientAsync };
const client: typeof RedisClientAsync = redisAsync.createClient();
// now you can use client async methods, i.e. client.getAsync, client.hgetAsync, client.hsetAsync, client.expireAsync...