Klasse in Definitionsdatei importieren (* d.ts)


96

Ich möchte die Express-Sitzungstypisierung erweitern, um die Verwendung meiner benutzerdefinierten Daten im Sitzungsspeicher zu ermöglichen. Ich habe ein Objekt, req.session.userdas eine Instanz meiner Klasse ist User:

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

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

Also habe ich meine own.d.tsDatei erstellt, um die Definition mit vorhandenen Express-Sitzungstypen zusammenzuführen:

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

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

Aber es funktioniert überhaupt nicht - VS Code und tsc sehen es nicht. Also habe ich eine Testdefinition mit einfachem Typ erstellt:

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

Und das Testfeld funktioniert in Ordnung, so dass der Import ein Problem verursacht.

Ich habe auch versucht, /// <reference path='models/user.ts'/>stattdessen Import hinzuzufügen, aber der tsc hat die Benutzerklasse nicht gesehen. Wie kann ich meine eigene Klasse in der Datei * d.ts verwenden?

BEARBEITEN : Ich habe tsc so eingestellt, dass beim Kompilieren Definitionsdateien generiert werden, und jetzt habe ich meine user.d.ts:

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

Und die eigene Typisierungsdatei zum Erweitern von Express Sesion:

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

Funktioniert aber immer noch nicht, wenn die Importanweisung oben steht. Irgendwelche Ideen?

Antworten:


272

Nach zwei Jahren TypeScript-Entwicklung habe ich es endlich geschafft, dieses Problem zu lösen.

Grundsätzlich gibt es in TypeScript zwei Arten von Deklarationen für Modultypen: "local" (normale Module) und ambient (global). Die zweite Art ermöglicht das Schreiben einer globalen Moduldeklaration, die mit der vorhandenen Moduldeklaration zusammengeführt wird. Was sind die Unterschiede zwischen diesen Dateien?

d.tsDateien werden nur dann als Umgebungsmoduldeklarationen behandelt, wenn sie keine Importe haben. Wenn Sie eine Importzeile angeben, wird diese jetzt als normale Moduldatei behandelt, nicht als globale, sodass das Erweitern der Moduldefinitionen nicht funktioniert.

Deshalb funktionieren alle hier diskutierten Lösungen nicht. Glücklicherweise können wir seit TS 2.9 Typen mithilfe der folgenden import()Syntax in die Deklaration globaler Module importieren :

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

Also macht die Linie import("./user").User;die Magie und jetzt funktioniert alles :)


4
Dies ist der richtige Weg, zumindest mit den neuesten Versionen von Typoskript
Jefferson Tavares

1
Dieser Ansatz ist die ideale Lösung, wenn Schnittstellen deklariert werden, die globale Module wie das Node- processObjekt erweitern.
Teffen Ellis

1
Vielen Dank, dies war die einzige klare Antwort auf die Behebung meiner Probleme mit der Erweiterung der Express Middleware!
Katsuke

Danke, hat mir sehr geholfen. Hatte keine andere Möglichkeit, dies in einem bestehenden Projekt zu tun. Ziehen Sie dies der Erweiterung der Request-Klasse vor. Dziękuję bardzo.
Christophe Geers

1
Vielen Dank @ Michał Lytek Ich frage mich, ob es eine offizielle Dokumentationsreferenz für diesen Ansatz gibt.
Gena

3

AKTUALISIEREN

Seit Typoskript 2.9 scheinen Sie in der Lage zu sein, Typen in globale Module zu importieren. Weitere Informationen finden Sie in der akzeptierten Antwort.

URSPRÜNGLICHE ANTWORT

Ich denke, das Problem, mit dem Sie konfrontiert sind, besteht mehr darin, Moduldeklarationen zu erweitern als die Klassentypisierung.

Der Export ist in Ordnung, wie Sie feststellen werden, wenn Sie versuchen, dies zu kompilieren:

// app.ts  
import { User } from '../models/user'
let theUser = new User('theLogin', 'thePassword')

Es scheint, als ob Sie versuchen, die Moduldeklaration von zu erweitern Express, und Sie sind wirklich nah dran. Dies sollte den Trick tun:

// index.d.ts
import { User } from "./models/user";
declare module 'express' {
  interface Session {
    user: User;
    uuid: string;
  }
}

Die Richtigkeit dieses Codes hängt jedoch natürlich von der ursprünglichen Implementierung der Express-Deklarationsdatei ab.


Wenn ich die import-Anweisung nach innen verschiebe, wird folgende Fehlermeldung angezeigt : Import declarations in a namespace cannot reference a module.. Wenn ich Ihren Code kopiere und einfüge, bekomme ich : Import or export declaration in an ambient module declaration cannot reference module through relative module name.. Und wenn ich versuche, einen nicht relativen Pfad zu verwenden, kann ich meine Datei nicht finden. Deshalb habe ich den Deklarationsordner in den Pfad node_modules und ad add verschoben, "declarations/models/user"aber die gesamte d.ts funktioniert immer noch nicht. In Intelisense wird keine eigene Erweiterung der Express-Sitzung angezeigt oder tsc.
Michał Lytek

Ich bin mit diesen Fehlern nicht vertraut, sorry. Vielleicht ist etwas anderes in Ihrem Setup? Ist das für Sie zu kompilieren? gist.github.com/pellejacobs/498c997ebb8679ea90826177cf8a9bad .
Pelle Jacobs

Auf diese Weise funktioniert es, funktioniert aber in einer echten App immer noch nicht. Ich habe dort ein Express-Anforderungsobjekt mit Sitzungsobjekt und es hat einen anderen Typ deklariert - im Namespace Express nicht Modul 'Express': github.com/DefinitelyTyped/DefinitelyTyped/blob/master/…
Michał Lytek

5
Das funktioniert auch bei mir nicht. Sobald ich die Importanweisungen zu meiner Datei tsd.d.ts hinzufüge, funktioniert die gesamte Datei nicht mehr. (Ich bekomme Fehler in der restlichen Anwendung für Dinge, die in dieser Datei definiert sind.)
Vern Jensen

5
Ich hatte das gleiche Problem. Es funktioniert, wenn Sie den Import in einem deklarierten Modul in Ihrer .d.ts verwenden: declare module 'myModule' {import { FancyClass } from 'fancyModule'; export class MyClass extends FancyClass {} }
Zunder

2

Dank der Antwort von Michał Lytek . Hier ist eine andere Methode, die ich in meinem Projekt verwendet habe.

Wir können es mehrmals importieren Userund wiederverwenden , ohne import("./user").Userüberall schreiben zu müssen , und es sogar erweitern oder erneut exportieren .

declare namespace Express {
  import("./user");  // Don't delete this line.
  import { User } from "./user";

  export interface Request {
    user: User;
    target: User;
    friend: User;
  }

  export class SuperUser extends User {
    superPower: string;
  }

  export { User as ExpressUser }
}

Habe Spaß :)


-1

Ist es nicht möglich, der Logik einfach zu folgen mit express-session:

own.d.ts::

import express = require('express');
import { User } from "../models/user";

declare global {
    namespace Express {
        interface Session {
            user: User;
            uuid: string;
        }
    }
}

In der Hauptsache index.ts:

import express from 'express';
import session from 'express-session';
import own from './types/own';

const app = express();
app.get('/', (req, res) => {
    let username = req!.session!.user.login;
});

Zumindest scheint dies ohne Probleme zu kompilieren. Den vollständigen Code finden Sie unter https://github.com/masa67/so39040108


1
Sie dürfen keine Deklarationsdateien importieren, da tscdiese nicht kompiliert werden. Sie sollen in der Zusammenstellung sein, aber nicht in der Ausgabe
Balint Csak
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.