นำเข้าคลาสในไฟล์นิยาม (* d.ts)


96

ฉันต้องการขยายการพิมพ์เซสชันด่วนเพื่ออนุญาตให้ใช้ข้อมูลที่กำหนดเองของฉันในพื้นที่จัดเก็บเซสชัน ฉันมีวัตถุ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 ได้อย่างไร

แก้ไข: ฉันตั้งค่า 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;
    }
}

แต่ยังไม่ทำงานเมื่อมีคำสั่งนำเข้าด้านบน ความคิดใด ๆ ?

คำตอบ:


271

หลังจากสองปีของการพัฒนา TypeScript ในที่สุดฉันก็สามารถแก้ปัญหานี้ได้

โดยพื้นฐานแล้ว TypeScript มีการประกาศประเภทโมดูลสองประเภท: "local" (โมดูลปกติ) และ ambient (global) ชนิดที่สองอนุญาตให้เขียนการประกาศโมดูลส่วนกลางที่รวมเข้ากับการประกาศโมดูลที่มีอยู่ ไฟล์นี้แตกต่างกันอย่างไร?

d.tsไฟล์จะถือว่าเป็นการประกาศโมดูลโดยรอบเฉพาะในกรณีที่ไม่มีการนำเข้าใด ๆ หากคุณระบุบรรทัดการนำเข้าตอนนี้จะถือว่าเป็นไฟล์โมดูลปกติไม่ใช่ไฟล์ส่วนกลางดังนั้นการเพิ่มข้อกำหนดโมดูลจึงไม่ทำงาน

นั่นคือสาเหตุที่วิธีแก้ปัญหาทั้งหมดที่เราพูดถึงในที่นี้ไม่ได้ผล แต่โชคดีเนื่องจาก TS 2.9 เราสามารถนำเข้าประเภทในการประกาศโมดูลส่วนกลางโดยใช้import()ไวยากรณ์:

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

ดังนั้นสายimport("./user").User;จึงใช้เวทมนตร์และตอนนี้ทุกอย่างทำงาน :)


4
นี่เป็นวิธีที่ถูกต้องอย่างน้อยก็ด้วย typescript เวอร์ชันล่าสุด
Jefferson Tavares

1
แนวทางนี้เป็นทางออกที่ดีที่สุดเมื่อประกาศอินเทอร์เฟซที่ขยายโมดูลส่วนกลางเช่นprocessอ็อบเจ็กต์ของโหนด
Teffen Ellis

1
ขอบคุณนี่เป็นคำตอบเดียวที่ชัดเจนในการแก้ไขปัญหาของฉันด้วยการขยาย Express Middleware!
Katsuke

ขอบคุณมากช่วยได้มาก ไม่มีวิธีอื่นในการทำเช่นนี้ในโครงการที่มีอยู่ ต้องการสิ่งนี้มากกว่าการขยายคลาสคำขอ Dziękuję bardzo
Christophe Geers

1
ขอบคุณ @ Michał Lytek ฉันสงสัยว่ามีเอกสารอ้างอิงอย่างเป็นทางการสำหรับแนวทางนี้หรือไม่?
Gena

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 "declarations/models/user"แต่ d.ts ทั้งหมดยังไม่ทำงาน - ไม่สามารถเห็นส่วนขยายของเซสชันด่วนของตัวเองใน intelisense หรือ tsc
Michał Lytek

ฉันไม่คุ้นเคยกับข้อผิดพลาดเหล่านี้ขออภัย อาจมีบางอย่างที่แตกต่างออกไปในการตั้งค่าของคุณ? สิ่งนี้จะรวบรวมให้คุณหรือไม่? gist.github.com/pellejacobs/498c997ebb8679ea90826177cf8a9bad
Pelle Jacobs

วิธีนี้ได้ผล แต่ยังใช้ไม่ได้ในแอปจริง ฉันต้องมีวัตถุคำขอด่วนกับวัตถุเซสชั่นและมันได้ประเภทอื่น ๆ ประกาศ - ใน namespace ด่วนไม่โมดูล 'แสดง': github.com/DefinitelyTyped/DefinitelyTyped/blob/master/...
Michał Lytek

5
มันไม่ได้ผลสำหรับฉันเช่นกัน เมื่อฉันเพิ่มคำสั่งนำเข้าในไฟล์ tsd.d.ts ของฉันไฟล์ทั้งหมดจะหยุดทำงาน (ฉันได้รับข้อผิดพลาดในส่วนที่เหลือของแอปพลิเคชันสำหรับสิ่งที่กำหนดไว้ในไฟล์นั้น)
Vern Jensen

5
ผมมีปัญหาเหมือนกัน. ใช้งานได้ถ้าคุณใช้การนำเข้าในโมดูลที่ประกาศไว้ภายใน. d.ts ของคุณ: declare module 'myModule' {import { FancyClass } from 'fancyModule'; export class MyClass extends FancyClass {} }
zunder

2

ขอบคุณกับคำตอบจากMichał Lytek นี่เป็นอีกวิธีหนึ่งที่ฉันใช้ในโครงการของฉัน

เราสามารถนำเข้า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จะไม่รวบรวมไฟล์เหล่านั้น พวกเขาตั้งใจจะอยู่ในการรวบรวม แต่ไม่ใช่ในผลลัพธ์
Balint Csak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.