Nhập lớp trong tệp định nghĩa (* d.ts)


97

Tôi muốn mở rộng kiểu đánh máy Phiên nhanh để cho phép sử dụng dữ liệu tùy chỉnh của tôi trong lưu trữ phiên. Tôi có một đối tượng req.session.userlà một phiên bản của lớp tôi User:

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

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

Vì vậy, tôi đã tạo own.d.tstệp của mình để hợp nhất định nghĩa với các kiểu đánh máy phiên nhanh hiện có:

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

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

Nhưng nó không hoạt động chút nào - VS Code và tsc không thấy nó. Vì vậy, tôi đã tạo định nghĩa thử nghiệm với kiểu đơn giản:

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

Và trường kiểm tra đang hoạt động tốt, vì vậy vấn đề nhập khẩu gây ra.

Tôi cũng đã thử thêm /// <reference path='models/user.ts'/>nhập thay vào đó nhưng tsc không thấy lớp Người dùng - làm cách nào để tôi có thể sử dụng lớp của riêng mình trong tệp * d.ts?

CHỈNH SỬA: Tôi đặt tsc để tạo tệp định nghĩa khi biên dịch và bây giờ tôi có user.d.ts của mình:

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

Và tệp nhập riêng để mở rộng Express Sesion:

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

Nhưng vẫn không hoạt động khi nhập câu lệnh trên đầu trang. Có ý kiến ​​gì không?

Câu trả lời:


276

Sau hai năm phát triển TypeScript, cuối cùng tôi đã giải quyết được vấn đề này.

Về cơ bản, TypeScript có hai loại khai báo kiểu mô-đun: "cục bộ" (mô-đun bình thường) và môi trường xung quanh (toàn cầu). Loại thứ hai cho phép viết khai báo mô-đun toàn cục được hợp nhất với khai báo mô-đun hiện có. Sự khác biệt giữa các tệp này là gì?

d.tscác tệp chỉ được coi là khai báo mô-đun môi trường xung quanh nếu chúng không có bất kỳ lần nhập nào. Nếu bạn cung cấp một dòng nhập, nó hiện được coi là một tệp mô-đun bình thường, không phải là tệp chung, vì vậy các định nghĩa mô-đun bổ sung không hoạt động.

Vì vậy, đó là lý do tại sao tất cả các giải pháp chúng ta đã thảo luận ở đây không hoạt động. Nhưng may mắn thay, vì TS 2.9, chúng tôi có thể nhập các kiểu vào khai báo mô-đun toàn cục bằng import()cú pháp:

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

Vì vậy, dòng import("./user").User;thực hiện điều kỳ diệu và bây giờ mọi thứ hoạt động :)


4
Đây là cách đúng đắn để làm điều đó, ít nhất là với các phiên bản gần đây của nguyên cảo
Jefferson Tavares

1
Cách tiếp cận này là giải pháp lý tưởng khi khai báo các giao diện mở rộng các mô-đun toàn cục như processđối tượng của Node .
Teffen Ellis

1
Cảm ơn, đây là câu trả lời rõ ràng duy nhất để khắc phục sự cố của tôi với Express Middleware mở rộng!
Katsuke

Thx, đã giúp rất nhiều. Không có cách nào khác để làm điều này trong một dự án hiện có. Thích điều này hơn là mở rộng lớp Yêu cầu. Dziękuję bardzo.
Christophe Geers

1
Cảm ơn bạn @ Michał Lytek Tôi tự hỏi có tài liệu tham khảo chính thức nào cho cách tiếp cận này không?
Gena

3

CẬP NHẬT

Kể từ typecript 2.9, bạn dường như có thể nhập các loại vào các mô-đun toàn cục. Xem câu trả lời được chấp nhận để biết thêm thông tin.

CÂU TRẢ LỜI GỐC

Tôi nghĩ rằng vấn đề bạn đang gặp phải là về việc tăng cường khai báo mô-đun, sau đó là nhập lớp.

Việc xuất là tốt, như bạn sẽ nhận thấy nếu bạn cố gắng biên dịch:

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

Có vẻ như bạn đang cố gắng tăng cường khai báo mô-đun của Express, và bạn thực sự đã kết thúc. Cái này cần phải dùng mẹo:

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

Tuy nhiên, tính đúng đắn của mã này tất nhiên phụ thuộc vào việc triển khai ban đầu của tệp khai báo nhanh.


Nếu tôi chuyển câu lệnh import bên tôi nhận được lỗi: Import declarations in a namespace cannot reference a module.. Nếu tôi copy-dán mã của bạn tôi nhận: Import or export declaration in an ambient module declaration cannot reference module through relative module name.. Và nếu tôi cố gắng sử dụng đường dẫn không tương đối, tôi không thể tìm thấy tệp của mình, vì vậy tôi đã chuyển thư mục khai báo sang đường dẫn thêm quảng cáo node_modules "declarations/models/user"nhưng toàn bộ d.ts vẫn không hoạt động - không thể xem phần mở rộng của riêng phiên express trong intelisense hoặc tsc.
Michał Lytek

Tôi không quen với những lỗi này, xin lỗi. Có thể có điều gì đó khác biệt trong thiết lập của bạn? Điều này có để biên dịch cho bạn? gist.github.com/pellejacobs/498c997ebb8679ea90826177cf8a9bad .
Pelle Jacobs,

Cách này hoạt động nhưng vẫn không hoạt động trong ứng dụng thực. Tôi có có một đối tượng yêu cầu rõ ràng với đối tượng session và nó đã loại khác tuyên bố - trong namespace Express không module 'thể hiện': github.com/DefinitelyTyped/DefinitelyTyped/blob/master/...
Michał Lytek

5
Nó cũng không hiệu quả với tôi. Khi tôi thêm các câu lệnh nhập vào tệp tsd.d.ts của mình, toàn bộ tệp sẽ ngừng hoạt động. (Tôi nhận được sai sót trong phần còn lại của ứng dụng của tôi cho những thứ được định nghĩa trong tập tin đó.)
Vern Jensen

5
Tôi đã từng gặp vấn đề tương tự. Nó hoạt động nếu bạn sử dụng nhập khẩu trong một mô-đun công bố trong vòng .d.ts của bạn: declare module 'myModule' {import { FancyClass } from 'fancyModule'; export class MyClass extends FancyClass {} }
zunder

3

Cảm ơn câu trả lời từ Michał Lytek . Đây là một phương pháp khác mà tôi đã sử dụng trong dự án của mình.

Chúng tôi có thể nhập Usersử dụng lại nó nhiều lần mà không cần ghi import("./user").Userở mọi nơi, và thậm chí có thể mở rộng hoặc tái xuất nó.

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

Chúc vui vẻ :)


-1

Không thể chỉ tuân theo logic với express-session:

own.d.ts:

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

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

Trong chính 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;
});

Ít nhất điều này dường như biên dịch mà không có bất kỳ vấn đề nào. Để biết mã đầy đủ, hãy xem https://github.com/masa67/so39040108


1
Bạn không được nhập các tệp khai báo, vì tscsẽ không biên dịch chúng. Chúng có nghĩa là trong bản biên dịch nhưng không có trong đầu ra
Balint Csak
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.