ค้นหาเว็บไซต์

วิธีการขยายโมดูล TypeScript โดยไม่ต้องเขียนทับมัน?


ฉันต้องการขยายอ็อบเจ็กต์คำขอ fastify เพื่อรวมข้อมูลเกี่ยวกับเซสชัน

ตามเอกสาร คู่มือนี้ควรเพียงพอแล้ว:

declare module "fastify" {
  interface Session {
    user_id: string
    other_key: your_prefer_type
    id?: number
  }
}

อย่างไรก็ตาม ถ้าฉันทำแบบนั้น การนำเข้าประเภททั้งหมดของฉันก็จะเริ่มล้มเหลว

src/factories/createServer.ts:189:19 - error TS2349: This expression is not callable.
  Type 'typeof import("fastify")' has no call signatures.

189   const fastify = Fastify({

ถ้าฉันทำอะไรแบบนี้,

declare module "fastify" {
  import Fastify from 'fastify';

  interface Session {
    user_id: string
    other_key: your_prefer_type
    id?: number
  }

  export = Fastify;
}

จากนั้นการนำเข้ามาตรฐานจะทำงานได้ แต่การนำเข้าอื่นทั้งหมดจะเสีย เช่น

src/factories/createServer.ts:11:8 - error TS2305: Module '"fastify"' has no exported member 'FastifyRequest'.

11   type FastifyRequest,

ฉันทำอะไรผิดตรงนี้หรือ?

คำตอบ #1

มีความแตกต่างที่ไม่ค่อยเป็นที่รู้จักระหว่างการประกาศ สคริปต์แบบไม่ใช่โมดูล/โกลบอล และโมดูลคลาสสิก:

ข้อกำหนดของ JavaScript ระบุว่าไฟล์ JavaScript ใด ๆ ที่ไม่มี export หรือ await ในระดับบนสุด ควรถูกพิจารณาว่าเป็นสคริปต์และไม่ใช่มอดูล

ภายในไฟล์สคริปต์ ตัวแปรและประเภทจะถูกประกาศให้อยู่ในขอบเขตทั่วโลกที่ใช้ร่วมกัน

สิ่งที่น่าจะเกิดขึ้นมากที่สุดในการลองครั้งแรกของคุณ คือคุณวางการประกาศในไฟล์ที่ไม่มีคำสั่ง import/export ดังนั้น TS จึงพิจารณามันเป็นการประกาศแบบ global ซึ่งจะเขียนทับ default export ของโมดูล "fastify"

// /src/globalDeclaration.d.ts
// NO top-level import/export
// Global declaration type
declare module "fastify" {
  interface Session {
    user_id: string;
    other_key: boolean; // your_prefer_type
    id?: number;
  }
}

// /src/index.ts
import Fastify from "fastify";

const fastify = Fastify(); // Error: Type 'typeof import("fastify")' has no call signatures.ts(2349)

สาธิตบน CodeSandbox: https://codesandbox.io/s/peaceful-resonance-fc1swm?file=/src/index.ts


ในการแก้ไขปัญหา ดังนั้นตามที่แนะนำในเอกสาร TS:

ถ้าคุณมีไฟล์ที่ปัจจุบันยังไม่มีการนำเข้า (imports) หรือส่งออก (exports) ใด ๆ แต่คุณต้องการให้ไฟล์นั้นถูกพิจารณาเป็นโมดูล ให้เพิ่มบรรทัดนี้:

export {};

ซึ่งจะเปลี่ยนไฟล์ให้เป็นโมดูลที่ไม่ส่งออกอะไรเลย

ดังนั้นลองเพิ่มการส่งออกว่าง (สามารถอยู่ที่ใดก็ได้ในไฟล์) เพื่อเปลี่ยนการประกาศให้เป็นการเพิ่มขยายโมดูลเท่านั้น

แต่ต้องแน่ใจว่าได้ทำการนำเข้าไฟล์โดยชัดเจน แม้ว่าจะไม่มีอะไรให้ "นำเข้าจาก" ก็ตาม: เราแค่ต้องการให้ TS ทำผลข้างเคียงของการเพิ่มโมดูล

// /src/moduleAugmentation.d.ts
export {}; // Make an import or export to turn the file into a module augmentation only

declare module "fastify" {
  interface Session {
    user_id: string;
    other_key: boolean; // your_prefer_type
    id?: number;
  }
}

// /src/index.ts
import Fastify, { FastifyRequest } from "fastify";
import fastifySession from "@fastify/session";
import fastifyCookie from "@fastify/cookie";

import "./moduleAugmentation"; // Now we have to explicitly import the module augmentation file, even just for its side effect

const fastify = Fastify();
fastify.register(fastifyCookie);
fastify.register(fastifySession, {
  secret: "a secret with minimum length of 32 characters"
});

fastify.addHook("preHandler", (request, reply, next) => {
  const b = request.session.other_key;
  //                        ^? (property) Session.other_key: boolean
  next();
});

ตัวอย่างสาธิตบน CodeSandbox: https://codesandbox.io/s/inspiring-phoebe-ly086x?file=/src/index.ts