Typescript references misunderstanding - reference

I have a TypeScript d.ts file which I'm referencing from another file, but for some reason the exported class definitions don't seem to be recognised.
foo.d.ts
export declare class MyClass {
constructor();
public MyFunc(id: number): void;
}
bar.ts
/// <reference path="typings/MyClass.d.ts" />
class BarClass {
private something: MyClass;
constructor(thing: MyClass) {
this.something = thing;
}
}
That's about as simple an example I can give, but when doing this I get Could not find symbol 'MyClass'
I'm sure this used to work prior to updating TypeScript to the latest version, but on checking the breaking changes, I can't see anything which would cause the issue.
Does anyone have any ideas here?

Remove the export keyword. i.e
export declare class MyClass {
constructor();
public MyFunc(id: number): void;
}
to
declare class MyClass {
constructor();
public MyFunc(id: number): void;
}
Reason: The export keyword at the root of the file is reserved for external modules. A video on external modules : http://www.youtube.com/watch?v=KDrWLMUY0R0&hd=1

Related

Awilix: Unable to resolve subclass with hard coded base class constructor parameters

I use the awilix library for achieving DI in my nodejs express project which used ES6 and nodejs 12.x.
Let's say I have a base class and a subclass as follows
class MyBaseClass {
#collectionName;
constructor(collectionName) {
this.#collectionName = collectionName;
}
getCollection() {
return this.#collection;
}
}
class MySubClass extends MyBaseClass {
constructor() {
super("users");
}
}
I use awilix.InjectionMode.CLASSIC. When I try to resolve and instance of MySubClass, I get a resolution error which basically says "Could not resolve 'collectionName'". MySubClass does not need constructor parameters to be passed in order to be instantiated. What am I doing wrong?

Cannot find name inside namespace

I'm trying to separate interfaces and implementations inside typescript, so I choose to use module feature. However, I always receive Cannot find name even when I use <reference path=.../>. Here is my code:
IUserService.ts
namespace Service {
export interface IUserService {
login(username: string, password: string): void;
}
}
UserService.ts
/// <reference path="./IUserService.ts" />
namespace Service {
export class UserService implements IUserService {
constructor() {}
}
Then tsc always complains that Cannot find name IUserService inside UserService.ts. I follow what the documentation said about namespace but it's not working for me. What should be the fix for this?
Two advices from the TypeScript handbook:
Do not use the /// <reference ... /> syntax;
Do not use namespaces and modules together. Node.js already provides modules, so you don't need namespaces.
Here is a solution:
// IUserService.d.ts
export interface IUserService {
login(username: string, password: string): void;
}
// UserService.ts
import { IUserService } from "./IUserService";
export class UserService implements IUserService {
constructor() {
}
login(username: string, password: string) {
}
}
You'll have to define a tsconfig.json file. The /// <reference ... /> statement is replaced by a configuration file (tsconfig.json) since TypeScript 1.5 (section "Lightweight, portable projects").
Related : How to use namespaces with import in TypeScript and Modules vs. Namespaces: What is the correct way to organize a large typescript project? .

Typescript - asserting imported object to class static property

Having these two files:
ErrorCodesEnum.ts:
export const ErrorCodesEnum =
{
generic: {
NOT_FOUND: 'NOT_FOUND',
},
//...
}
Main.js:
import {ErrorCodesEnum} from "../enum/ErrorCodesEnum";
export class ErrorFactory {
public static CODES: ErrorCodesEnum; //error: see below.
}
I am getting following error in Main.js:
Public static property 'CODES' of exported class has or is using private name 'ErrorCodesEnum'.
Is there a way I can use some imported module in static class property?
Thanks.
You can't use ErrorCodesEnum as a type because it's not a type. You can reference its type using the typeof operator:
public static CODES: typeof ErrorCodesEnum;
Or if you're just trying to assign ErrorCodesEnum to CODES, just omit the type and set the value directly:
public static CODES = ErrorCodesEnum;

Get file path of imported module

I'm writing a class decorator for my controllers. It looks like:
export function Controller<T extends { new(...args: any[]): {} }> (ctor: T) {
return class extends ctor {
public readonly name = name;
}
}
ctor is a constructor of a class decorated with #Controller.
Full path to the controller's file is src/modules/{module}/controllers/{ctrl}Controller.ts. I need to get parts in curly braces and concatenate them into {module}.{ctrl}.
To do so I need a filepath of module from which ctor is imported. How can I obtain it?
There is no way to get file path information from ctor parameter. It's just a function that was defined somewhere.
Basically, module and ctrl preferably have to be provided to controller class on registration, since the path is known at this moment, i.e.:
for (const filename of filenames) {
const Ctrl = require(filename).default;
const [moduleName, ctrlName] = parseCtrlFilename(filename);
Ctrl._module = moduleName;
Ctrl._name = ctrlName;
}
The only and hacky workarount is to get file path of a place where Controller was called. This is achieved by getting stacktrace, e.g:
const caller = require('caller-callsite');
export function Controller<T extends { new(...args: any[]): {} }> (ctor: T) {
const fullPath = caller().getFileName();
...
}
The problem is that it's the path where Controller is called:
.../foo.ts
#Controller
export class Foo {...}
.../bar.ts
import { Foo } from '.../foo.ts';
// fullPath is still .../foo.ts
export class Bar extends Foo {}
A less hacky and more reliable way is to provide file path explicitly from the module where it is available:
#Controller(__filename)
export class Foo {...}
There is import.meta proposal which is supported by TypeScript. It depends on Node project configuration because it works with esnext target:
#Controller(import.meta)
export class Foo {...}
import.meta that was passed to #Controller can be consumed as meta.__dirname.

Typescript: Override static factory method of parent Class in Child method

I'm running into some problems with dependency injection with Typescript. On every Class that I add a factory static method where all dependencies are set. I do this for testing purposes so that I'm still able to use the TDD approach.
Now I'm running into some problems with overriding the factory method of the parent class in a child class. Example:
interface DepsA {
a: string
}
interface DepsB extends DepsA {
b: Child1
}
class Parent {
constructor(protected deps: DepsA | DepsB) {}
public static factory<T extends Parent>() {
return new this({a: 'This is a nice Dependency'}) as T
}
}
class Child1 extends Parent {}
class Child2 extends Parent {
public static factory() {
return new this({a: 'This is a nice Dependency', b: Child1.factory()})
}
}
const child1 = Child1.factory<Child1>()
const child2 = Child2.factory()
The error what I receive is:
[ts]
Class static side 'typeof Child2' incorrectly extends base class static side 'typeof Parent'.
Types of property 'factory' are incompatible.
Type '() => Child2' is not assignable to type '<T extends Parent>() => T'.
Type 'Child2' is not assignable to type 'T'.
I know why I get the error, but have at this point no idea anymore how to fix it, otherwise than renaming the factory static method in Child2.
UPDATE: A Related bug report to this problem, that explains automatically why I use a Generic on the factory method is: #26298
First, there's a pre-defined conditional type called InstanceType which could help you to infer the class type from the static member:
public static factory<T extends typeof Parent>(this: T) {
return new this({ a: 'This is a nice Dependency' }) as InstanceType<T>
}
Second, if you override a method, static or not, in a child class, it should have a compatible signature, including the generic stuff.
Consequently, your code block could look like this (see in Typescript Playground):
interface DepsA {
a: string
}
interface DepsB extends DepsA {
b: Child1
}
class Parent {
constructor(public deps: DepsA | DepsB) {}
public static factory<T extends typeof Parent>(this: T) {
return new this({ a: 'This is a nice Dependency' }) as InstanceType<T>
}
}
class Child1 extends Parent {}
class Child2 extends Parent {
public static factory<T extends typeof Parent>(this: T) {
return new this({a: 'This is a nice Dependency', b: Child1.factory()}) as InstanceType<T>
}
}
const child1 = Child1.factory() // Type: Child1
const child2 = Child2.factory() // Type: Child2
From there, returning the proper deps type, rather than an union, would also be possible in non static members, using as this["deps"]. But you'd have to revamp a bit your code.
Hope it helps ;-)

Resources