Import class in definition file (*d.ts)

Michał Lytek picture Michał Lytek · Aug 19, 2016 · Viewed 41k times · Source

I want to extend Express Session typings to allow use my custom data in session storage. I have an object req.session.user which is an instance of my class User:

export class User {
    public login: string;
    public hashedPassword: string;

    constructor(login?: string, password?: string) {
        this.login = login || "" ;
        this.hashedPassword = password ? UserHelper.hashPassword(password) : "";
    }
}

So i created my own.d.ts file to merge definition with existing express session typings:

import { User } from "./models/user";

declare module Express {
    export interface Session {
        user: User;
    }
}

But it's not working at all - VS Code and tsc don't see it. So I created test definition with simple type:

declare module Express {
    export interface Session {
        test: string;
    }
}

And the test field is working ok, so the import cause problem.

I also tried to add /// <reference path='models/user.ts'/> instead import but the tsc didn't see the User class - how can I use my own class in *d.ts file?

EDIT: I set tsc to generate definition files on compile and now I have my user.d.ts:

export declare class User {
    login: string;
    hashedPassword: string;
    constructor();
    constructor(login: string, password: string);
}

And the own typing file for extending Express Sesion:

import { User } from "./models/user";
declare module Express {
    export interface Session {
        user: User;
        uuid: string;
    }
}

But still not working when import statement on top. Any ideas?

Answer

Michał Lytek picture Michał Lytek · Jun 30, 2018

After two years of TypeScript development, I've finally managed to solve this problem.

Basically, TypeScript has two kind of module types declaration: "local" (normal modules) and ambient (global). The second kind allows to write global modules declaration that are merged with existing modules declaration. What are the differences between this files?

d.ts files are treated as an ambient module declarations only if they don't have any imports. If you provide an import line, it's now treated as a normal module file, not the global one, so augmenting modules definitions doesn't work.

So that's why all the solutions we discussed here don't work. But fortunately, since TS 2.9 we are able to import types into global modules declaration using import() syntax:

declare namespace Express {
  interface Request {
    user: import("./user").User;
  }
}

So the line import("./user").User; does the magic and now everything works :)