วิธีการขยายโมดูล 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
