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 ...
}
}
Related
I am new to typescript and not that familiar.
I was reading this blog on web and trying to comprehend the code
Here the author have created a simple route
// /lib/routes/crmRoutes.ts
import {Request, Response} from "express";
export class Routes {
public routes(app): void {
app.route('/')
.get((req: Request, res: Response) => {
res.status(200).send({
message: 'GET request successfulll!!!!'
})
})
}
}
Which is he is using like this in a kind of main-entry point file
// /lib/app.ts
import * as express from "express";
import * as bodyParser from "body-parser";
import { Routes } from "./routes/crmRoutes";
class App {
public app: express.Application;
public routePrv: Routes = new Routes();
constructor() {
this.app = express();
this.config();
this.routePrv.routes(this.app);
}
private config(): void{
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: false }));
}
}
From the definition I read on web, Classes are also like interface in typescript, so this line
public routePrv: Routes = new Routes();
Here, Routes is an interface for routePrv:? And from my vague understanding, interface usually looks like this
interface Person {
name: string;
age: number;
}
So my question is that, How can a class be an interface and how will our interface look like in case of
public routePrv: Routes = new Routes();
Can someone please explain the same?
You are confusing Interfaces and Types.
An Interface (interface MyInterface {}) is used to express what an Object has to look like. After typescript transpilation, interfaces are removed. They are more like "hints" for typescript. An example of its usage is this:
interface MyInterface {
myProp:number;
}
const myConst = {myProp: 0}; // implicitly implements MyInterface
const myConst2:MyInterface = {myProp: 0}; // Explicitly tells typescript the type of "myConst2" should be "MyInterface"
const myConst2:MyInterface = {}; // Error, does not implement "MyInterface" correctly
function myFunc(input:MyInterface) {/* Do something */}
myFunc(myConst); // Works
myFunc(myConst2); // Works
myFunc({}); // Fails
Classes (class MyClass {}) boil down to Constructor Functions. They are a way of constructing an Object. Since they describe how to construct an Object and thus describe the constructed Object's shape, they can also be used to describe the type of an Object. Since Classes are Constructor Functions, they do something during runtime. Hence, only their usages as types are removed.
// You can explicitly implement the interface or explicitly, doesn't matter for its usage.
// Explicitly just tells typescript that this class MUST implement the interface
class MyClass /* implements MyInterface */ {
constructor(public myProp?:number = 0) {}
}
const myClassConst = new MyClass(); // works
const myClassConst2:MyClass = new MyClass(); // works
const myClassConst3:MyInterface = new MyClass(); // works
myFunc(myClassConst); // works
myFunc(myClassConst2); // works
myFunc(myClassConst3); // works
function myFunc2(input:MyClass) { /* Do something */ }
myFunc2(myClassConst); // works
myFunc2(myClassConst2); // works
myFunc2(myClassConst3); // works
As per Request, another example of what does not work:
class MyOtherClass {
constructor(public myProp:number) {}
}
const myOthClassConst = new MyOtherClass(); // works
const myOthClassConst2:MyClass = new MyOtherClass(); // fails, type mismatch
const myOthClassConst3:MyInterface = new MyOtherClass(); // fails, type mismatch
function myFunc3(input:MyOtherClass) {}
myFunc3(myOthClassConst2); // works
myFunc3(myClassConst); // fails, type mismatch
Edit: Rephrased the explanation due to ambigious meaning (thanks to #Bergi for pointing that out).
Here is the next JavaScript class structure:
// data.service.ts
export class DataService {
public url = environment.url;
constructor(
private uri: string,
private httpClient: HttpClient,
) { }
getAll() {}
getOne(id: number) {}
create(data: any) {}
// etc...
}
Next is the general data model what can use the DataService's methods to communicate the server:
// Model.model.ts
import './data.service';
export class Model extends DataService {
all() {}
get() {
// parse and make some basic validation on the
// DataService.getOne() JSON result
}
// etc...
}
And finally I create a specific data model based on Model.model.ts:
// User.model.ts
import './Model.model.ts';
export class User extends Model {
id: number;
name: string;
email: string;
init() {
// make specific validation on Model.get() result
}
}
If I use the User class in my code, I can call the DataService's getAll() function directly if I want. But this is not a good thing, because in this case I miss the built-in validations.
How can I block the method inheritance on a class?
I'm looking for something like PHP's static method. The child class can use the methods, but his child can't.
I want something like this:
const dataService = new DataService();
dataService.getAll(); // void
const model = new Model();
model.getAll(); // undefined
model.all(); // void
const user = new User();
user.getAll(); // undefined
user.all(); // void
Is there any way to do this?
You can prevent it to be built when you call it by adding private keyword to the function private getAll() {}. But private is a TypeScript feature, not Javascript, so if you force it to be built, it is still callable. There'll be no way to totally prevent it this moment.
So if you want it to be prevented in TypeScript, just add private keyword there. But it is not buildable, not return undefined as you expect. Otherwise, just replace the function with an undefined-returned function on children classes
With your code as shown and use case as stated, the only way to get the behavior you want is not to make Model extend DataService at all. There is a subsitution principle which says that if Model extends DataService, then someone should be able to treat a Model instance exactly as they would treat a DataService instance. Indeed, if a Model is a special type of DataService, and someone asks for a DataService instance, you should be able to give them a Model. It's no fair for you to tell them that they can't call the getAll() method on it. So the inheritance tree can't work the way you have it. Instead you could do something like this:
// ultimate parent class of both DataService and Model/User
class BaseDataService {
getOne(id: number) { }
create(data: any) { }
// etc...
}
// subclass with getAll()
class DataService extends BaseDataService {
getAll() {}
}
// subclass without getAll()
class Model extends BaseDataService {
all() { }
get() { }
// etc...
}
class User extends Model {
id!: number;
name!: string;
email!: string;
init() { }
}
const dataService = new DataService();
dataService.getAll(); // void
const model = new Model();
model.getAll(); // error
model.all(); // okay
const user = new User();
user.getAll(); // error
user.all(); // okay
That works exactly as you've specified. Perfect, right?
Well, I get the sinking feeling that you will try this and get upset that you cannot call this.getAll() inside the implementation of Model or User... probably inside the suspiciously-empty body of the all() method in Model. And already I'm getting the urge to defensively point to the Minimum, Complete, and Verifiable Example article, since the question as stated doesn't seem to require this.
If you do require that, you still can't break the substitution principle. Instead I'd suggest making getAll() a protected method, and expose an all() method on DataService:
class DataService {
getOne(id: number) { }
create(data: any) { }
protected getAll() { }
all() {
this.getAll();
}
// etc...
}
class Model extends DataService {
all() {
this.getAll();
}
get() { }
// etc...
}
class User extends Model {
id!: number;
name!: string;
email!: string;
init() { }
}
const dataService = new DataService();
dataService.getAll(); // error
dataService.all(); // okay
const model = new Model();
model.getAll(); // error
model.all(); // okay
const user = new User();
user.getAll(); // error
user.all(); // okay
and live with the fact that getAll() is a purely internal method never meant to see the light of day.
Okay, hope one of those helps; good luck!
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...
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...
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...