Імпортувати клас у файл визначення (* d.ts)


97

Я хочу розширити введення даних Express Session, щоб дозволити використовувати власні дані у сховищі сеансів. У мене є об'єкт, req.session.userякий є екземпляром мого класу User:

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

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

Тож я створив свій own.d.tsфайл для злиття визначення з існуючими експрес-типізаціями сеансів:

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

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

Але це взагалі не працює - VS Code і tsc цього не бачать. Тож я створив визначення тесту з простим типом:

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

І тестове поле працює нормально, тому імпорт спричиняє проблему.

Я також намагався додати /// <reference path='models/user.ts'/>замість цього імпорт, але tsc не побачив клас User - як я можу використовувати власний клас у файлі * d.ts?

EDIT: Я встановив tsc для генерації файлів визначень при компіляції, і тепер у мене є user.d.ts:

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

І власний файл набору тексту для розширення Express Sesion:

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

Але все одно не працює при імпортуванні зверху. Будь-які ідеї?

Відповіді:


275

Після двох років розробки TypeScript мені нарешті вдалося вирішити цю проблему.

В основному, TypeScript має два типи оголошень типів модулів: «локальний» (звичайні модулі) та зовнішній (глобальний). Другий вид дозволяє писати глобальну декларацію модулів, які об'єднані з існуючими деклараціями модулів. Які відмінності між цими файлами?

d.tsфайли розглядаються як декларації модуля зовнішнього середовища, лише якщо вони не мають імпорту. Якщо ви надаєте рядок імпорту, він тепер розглядається як звичайний файл модуля, а не як загальний, тому збільшення модулів у визначеннях не працює.

Ось чому всі рішення, про які ми тут говорили, не працюють. Але на щастя, починаючи з TS 2.9, ми можемо імпортувати типи в глобальну декларацію модулів із використанням import()синтаксису:

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

Тож лінія import("./user").User;робить магію і тепер все працює :)


4
Це правильний спосіб це зробити, принаймні з останніми версіями машинопису
Джефферсон Таварес

1
Цей підхід є ідеальним рішенням при оголошенні інтерфейсів, що розширюють глобальні модулі, такі як processоб'єкт Node .
Теффен Елліс,

1
Дякую, це була єдина чітка відповідь на вирішення моїх проблем із розширенням Express Middleware!
Кацуке,

Thx, дуже допоміг. Не мав іншого способу зробити це в існуючому проекті. Віддайте перевагу цьому перед розширенням класу Request. Dziękuję bardzo.
Крістоф Гірс,

1
Дякую @ Michał Lytek. Цікаво, чи є в офіційному документі посилання на цей підхід?
Гена

3

ОНОВЛЕННЯ

Починаючи з TypeScript 2.9, ви, здається, можете імпортувати типи в глобальні модулі. Дивіться прийняту відповідь для отримання додаткової інформації.

ОРИГІНАЛЬНА ВІДПОВІДЬ

Я думаю, що проблема, з якою ви стикаєтесь, полягає більше в збільшенні оголошень модулів, а не в наборі класів.

Експорт - це нормально, як ви помітите, якщо спробуєте скомпілювати це:

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

Здається, ви намагаєтеся збільшити декларацію модуля Express, і ви справді близькі. Це повинно зробити трюк:

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

Однак правильність цього коду, звичайно, залежить від оригінальної реалізації експрес-файлу декларації.


Якщо перемістити оператор імпорту всередині я отримую повідомлення про помилку: Import declarations in a namespace cannot reference a module.. Якщо я копіювати-вставити код , який я отримав: Import or export declaration in an ambient module declaration cannot reference module through relative module name.. І якщо я спробую використати не відносний шлях, я не можу знайти свій файл, тому я перемістив папку оголошень до node_modules ad add path, "declarations/models/user"але все-таки весь d.ts не працює - не бачу власного розширення експрес-сесії в intelisense або tsc.
Міхал Литек

Я не знайомий з цими помилками, вибачте. Можливо, у вашому налаштуванні є щось інше? Це для вас скомпілювати? gist.github.com/pellejacobs/498c997ebb8679ea90826177cf8a9bad .
Pelle Jacobs

Таким чином, це працює, але все ще не працює в реальному додатку. У мене є об’єкт експрес-запиту з об’єктом сеансу, і в ньому оголошено інший тип - у просторі імен Express не модуль „express”: github.com/DefinitelyTyped/DefinitelyTyped/blob/master/…
Міхал Литек

5
Це теж не працює у мене. Як тільки я додаю оператори імпорту до мого файлу tsd.d.ts, весь файл перестає працювати. (Я отримую помилки в решті моєї програми щодо речей, визначених у цьому файлі.)
Верн Дженсен,

5
У мене була та сама проблема. Це працює, якщо ви використовуєте імпорт у оголошеному модулі у вашому .d.ts: declare module 'myModule' {import { FancyClass } from 'fancyModule'; export class MyClass extends FancyClass {} }
zunder

3

Завдяки відповіді від Міхала Литека . Ось ще один метод, який я використав у своєму проекті.

Ми можемо імпортувати Userта використовувати повторно кілька разів, не писаючи import("./user").Userскрізь, і навіть розширити його або реекспортувати .

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 }
}

Веселіться :)


-1

Чи не можна просто слідувати логіці за допомогою express-session:

own.d.ts:

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

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

В основному 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;
});

Принаймні це, здається, складається без жодних проблем. Повний код див. На https://github.com/masa67/so39040108


1
Ви не повинні імпортувати файли декларацій, оскільки tscне збиратимете їх. Вони мають бути в компіляції, але не у вихідних даних
Балінт Цак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.