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? .
Related
Reading the NestJS docs on the MongoDB technique, I've came along a confusing example:
#Injectable()
export class CatsService {
constructor(#InjectModel(Cat.name) private catModel: Model<CatDocument>) {}
async create(createCatDto: CreateCatDto): Promise<Cat> {
const createdCat = new this.catModel(createCatDto);
return createdCat.save();
}
async findAll(): Promise<Cat[]> {
return this.catModel.find().exec();
}
}
The line that confuses me is the constructor one; where to #InjectModel is given Cat.name. But, in the cats.schema.ts file, there's no inheritance from another class, nor any valorised static property with that name:
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type CatDocument = Cat & Document;
#Schema()
export class Cat {
#Prop()
name: string;
#Prop()
age: number;
#Prop()
breed: string;
}
export const CatSchema = SchemaFactory.createForClass(Cat);
Am I missing something or could it be a "bug" in the docs?
Cat.name in this case refers to the inherent static property name that all classes have (really all functions have). This gives us a built in constant that we can refer to without having to write our own, but if you'd prefer you can use the string 'Cat' as well. In this case, Cat.name is the string 'Cat', just a different way to reference it that is built in to all classes (and functions).
JavaScript docs on Function.name
I am working on an API with NestJS, and because I have DTO's I am using an AutoMapper (made by #nartc and/or nestjsx), I have tried to make my example as small as I could with the Foo example, because I use multiple files.
This is my module:
// foo.module.ts
import { Module } from "#nestjs/common";
import { MongooseModule } from "#nestjs/mongoose";
import { Foo, FooSchema } from "./foo.entity.ts";
import { FooController } from "./foo.controller.ts";
import { FooService } from "./foo.service.ts";
import { FooProfile } from "./foo.profile.ts";
#Module({
imports: [
MongooseModule.forFeature([
{
name: Foo.name,
schema: FooSchema,
collection: "foos",
}
])
// FooProfile <-- if I uncomment this, the program will give the error (shown at the bottom of this question)
],
controllers: [FooController],
providers: [FooProivder],
})
export class FooModule {}
This is my entity:
// foo.entity.ts
import { Schema, SchemaFactory, Prop } from "#nestjs/mongoose";
import { Document } from "mongoose";
#Schema()
export class Foo extends Document { // if I remove the `extends Document` it works just fine
#Prop({ required: true })
name: string;
#Prop()
age: number
}
export const FooSchema = SchemaFactory.createForClass(Foo);
This is my DTO:
// foo.dto.ts
export class FooDTO {
name: string;
}
This is my controller:
// foo.controller.ts
import { Controller, Get } from "#nestjs/common";
import { InjectMapper, AutoMapper } from "nestjsx-automapper";
import { Foo } from "./foo.entity";
import { FooService } from "./foo.service";
import { FooDTO } from "./dto/foo.dto";
#Controller("foos")
export class FooController {
constructor(
private readonly fooService: FooService
#InjectMapper() private readonly mapper: AutoMapper
) {}
#Get()
async findAll() {
const foos = await this.fooService.findAll();
const mappedFoos = this.mapper.mapArray(foos, Foo, FooDTO);
// ^^ this throws an error of the profile being undefined (duh)
return mappedFoos;
}
}
This is my profile:
// foo.profile.ts
import { Profile, ProfileBase, InjectMapper, AutoMapper } from "nestjsx-automapper";
import { Foo } from "./foo.entity";
import { FooDTO } from "./foo.dto";
#Profile()
export class FooProfile extends ProfileBase {
constructor(#InjectMapper() private readonly mapper: AutoMapper) {
// I've read somewhere that the `#InjectMapper() private readonly` part isn't needed,
// but if I exclude that, it doesn't get the mapper instance. (mapper will be undefined)
super();
this.mapper.createMap(Foo, FooDTO);
}
}
If I uncomment the line I highlighted in the module, it will result in the following error..
[Nest] 11360 - 2020-08-18 15:53:06 [ExceptionHandler] Cannot read property 'plugin' of undefined +1ms
TypeError: Cannot read property 'plugin' of undefined
at Foo.Document.$__setSchema ($MYPATH\node_modules\mongoose\lib\document.js:2883:10)
at new Document ($MYPATH\node_modules\mongoose\lib\document.js:82:10)
at new Foo($MYPATH\dist\foo\foo.entity.js:15:17)
I have also referred to this answer on stackoverflow, but that doesn't work for me either. I have also combined that with the documentation, but with no luck.. How would I get the AutoMapper to register my profiles?
Update
The error seems to originate from the foo entity, if I remove the extends Document and the Schema(), Prop({ ... }) from the class it works fine, it seems like I have to inject mongoose or something?
In your module, just import the path to the profile like below:
import 'relative/path/to/foo.profile';
By importing the path to file, TypeScript will include the file in the bundle and then the #Profile() decorator will be executed. When #Profile() is executed, AutoMapperModule keeps track of all the Profiles then when it's turn for NestJS to initialize AutoMapperModule (with withMapper() method), AutoMapperModule will automatically add the Profiles to the Mapper instance.
With that said, in your FooProfile's constructor, you'll get AutoMapper instance that this profile will be added to
#Profile()
export class FooProfile extends ProfileBase {
// this is the correct syntax. You would only need private/public access modifier
// if you're not going to use this.mapper outside of the constructor
// You DON'T need #InjectMapper() because that's Dependency Injection of NestJS.
// Profile isn't a part of NestJS's DI
constructor(mapper: AutoMapper) {
}
}
The above answer will solve your problems with AutoMapper. As far as your Mongoose problem, I would need a sample repro to tell for sure. And also, visit our Discord for this kind of question.
What worked for me.
1. Updated all the absolute paths for models, schemas, entities (is easy if you search for from '/src in your projects, and update all the routes to relative paths)
from:
import { User } from 'src/app/auth/models/user/user.entity';
to:
import { User } from './../../auth/models/user/user.entity';
2. mongoose imports:
from:
import mongoose from 'mongoose';
to:
import * as mongoose from 'mongoose';
3. Remove validation pipe if you don't use it. For some reason (I think i don't use them on the controller, I didn't investigate, I've removed from one controller the Validation Pipe) so If you have this try it:
from:
#Controller('someroute')
#UsePipes(new ValidationPipe())
export class SomeController {}
to:
#Controller('someroute')
export class SomeController {}
I hope my solution worked for you ^_^
I have a Model class (in nodejs) and I want to implement it from AuthorizationCodeModel.
I want WebStorm detect that my model implemented the interface and suggest me auto complete
Interface.
Model:
/**
* #implements AuthorizationCodeModel
*/
class Model {
}
the #implements AuthorizationCodeModel is not working. How can I use JSDoc?
example interface in typescript
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number) { }
}
https://www.typescriptlang.org/docs/handbook/interfaces.html
What's the difference between 'extends' and 'implements' in TypeScript
JSDOC examples: http://usejsdoc.org/tags-implements.html
If autocomplete in Webstorm not worked, try to set reference path in js file
/// <reference path="components/someClass.js"/>
/// <reference path="components/someInterface.js"/>
/// <reference path="components/AuthorizationCodeModel.js"/>
/**
* #implements AuthorizationCodeModel
*/
class Model { }
Reference paths also use for autocomplete in some popular IDEs
https://madskristensen.net/blog/the-story-behind-_referencesjs
https://www.jetbrains.com/help/webstorm/configuring-javascript-libraries.html
https://intellij-support.jetbrains.com/hc/en-us/community/posts/206352279-How-to-not-reference-other-js-files
intellisense and code complete for DefinitelyTyped (TypeScript type definitions) on WebStorm IDE
I am currently working on my first Typescript project. After going through the official documentation and watching some courses (egghead), I thought it was time to write real code - not just samples.
I am using:
typescript#rc (~2.0.2)
Visual Studio Code (~1.4.0)
Windows 10
So I am working on a node module. My question here is about code structuring.
Here is how my project looks like:
src/
|____ core/
| |____ core.ts
| |____ components.ts
| |____ systems.ts
|____ input/
| |____ input.system.ts
| |____ keyboard.ts
|____ app.ts
Below is a sample of each files:
core.ts
/// <reference path="../core/components.ts" />
namespace Core {
export interface IEntity {
x: number
y: number
components: [Components.IComponent]
update(dt: number): void
}
export class Entity implements IEntity {
x: number
y: number
components: [Components.IComponent]
update(dt: number): void{
// do something with the coordinates
}
}
}
components.ts
namespace Components{
export interface IComponent {
update(dt: number): void
// some other stuff here...
}
}
systems.ts
namespace Systems{
export interface ISystem{
update(dt: number): void
// some other stuff here...
}
}
input.system.ts
/// <reference path="../core/systems.ts" />
namespace Systems{
export class InputSystem implements ISystem{
update(dt: number): void{
// do something here
}
// some other stuff here...
}
}
keyboard.ts
/// <reference path="../core/components.ts" />
namespace Components{
export class Keyboard implements IComponent{
update(dt: number): void{
// do something here - Catch key up / down
}
// some other stuff here...
}
}
app.ts
/// <reference path="./core/core.ts" />
/// <reference path="./core/components.ts" />
/// <reference path="./core/systems.ts" />
/// <reference path="./input/input.system.ts" />
/// <reference path="./input/keyboard.ts" />
export = {Core, Components, Systems}
What I want to do here is having 3 main namespaces Core, Components and Systems. Then if in another project this module is imported, we could do something like:
other.module.ts
// load module from node
import * as mymodule from "mymodule"
module A {
class A extends mymodule.Core.Entity{
constructor() {
this.components.push(new mymodule.Components.Keyboard());
}
}
export function main(): void{
var systems: [mymodule.Systems.ISystem];
systems.push(new mymodule.Systems.InputSystem());
// other systems could be pushed Here
for(var system in systems){
system.update(0.5);
}
}
}
The error that I am getting is in app.ts where for all namespaces compiler says:
cannot re export name that is not define
Is there something wrong with what I am doing?
I was also wondering if I should export using default in app.ts? like:
export default {Core, Components, Systems}
Would that help when importing my module?
Thanks,
Ed.
Not sure if my question was clear enough. I found a solution though, and would like your opinion.
So, I removed all the namespaces from my files. Which means that now all my files are modules.
Then at the root of my solution I create three files:
_core.ts
export * from "./core/core"
_components.ts
export * from "./core/components"
export * from "./input/keyboard"
_systems.ts
export * from "./core/systems"
export * from "./input/input.controller"
Now in my app.ts I can simply import those new files, like this.
import * as Core from "./_core"
import * as Components from "./_components.ts"
import * as Systems from "._/systems.ts"
export = {Core, Components, Systems}
What do you think? Any potential issue by doing so?
Thanks,
Ed.
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