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

typescript - ไม่สามารถขยาย express request


ฉันกำลังพยายามขยายอินเทอร์เฟซ Request ของ Express ดังนี้:

import express, { Request, Response } from 'express';
interface IRequest extends Request {
  user: {
    id: string;
  }
}

const router = express.Router();

router.get('/', auth, async (req: IRequest, res: Response) => {
  try {
    const user = await User.findById(req.user.id).select('-password');
    res.json(user);
  } catch (e) {
    console.error((e as Error).message);
    res.status(500).send('Server Error');
  }
});

แต่ฉันได้รับข้อผิดพลาดดังต่อไปนี้:

ไม่มีการโอเวอร์โหลดที่ตรงกับการเรียกนี้ การโอเวอร์โหลด 1 จาก 3, '(path: PathParams, ...handlers: RequestHandler[]): Router', เกิดข้อผิดพลาดดังต่อไปนี้ อาร์กิวเมนต์ประเภท '(req: IRequest, res: Response) => Promise' ไม่สามารถกำหนดให้กับพารามิเตอร์ประเภท 'RequestHandler' ได้ ประเภทของพารามิเตอร์ 'req' และ 'req' ไม่เข้ากัน คุณสมบัติ 'user' ไม่มีในประเภท 'Request' แต่จำเป็นในประเภท 'IRequest'. โอเวอร์โหลด 2 จาก 3, '(path: PathParams, ...handlers: RequestHandlerParams[]): Router', พบข้อผิดพลาดดังนี้ ประเภทของอาร์กิวเมนต์ '(req: IRequest, res: Response) => Promise' ไม่สามารถกำหนดค่าให้กับพารามิเตอร์ประเภท 'RequestHandlerParams' ได้ ประเภท '(req: IRequest, res: Response) => Promise' ไม่สามารถกำหนดค่าให้กับประเภท 'RequestHandler' ได้.ts(2769)

คำตอบ #1

Typescript จะไม่ให้คุณทำ เพราะแม้ว่าคุณจะพิมพ์มันแบบนี้:

router.get('/', auth, async (req: IRequest, res: Response) => {

Typescript จะยังคงสมมติว่า express จะส่งออก Request และไม่ใช่ IRequest

อย่างไรก็ตาม เนื่องจากคุณอาจกำลังทำการ monkey-patching กับวัตถุ request คุณสามารถใช้ declaration merging เพื่อเพิ่มความสามารถให้กับวัตถุ request ภายในของ Express ได้:

https://www.typescriptlang.org/docs/handbook/declaration-merging.html

ถ้าคุณไม่ต้องการเปลี่ยนแปลงนี้ในระดับทั่วโลกกับ Request คุณก็สามารถใช้การยืนยันเพื่อให้แน่ใจว่า มันเป็น IRequest จริงๆ:

router.get('/', auth, async (req: request, res: Response) => {
  try {
    assertIRequest(req);
    const user = await User.findById(req.user.id).select('-password');
    res.json(user);
  } catch (e) {
    console.error((e as Error).message);
    res.status(500).send('Server Error');
  }
});

function assertIRequest(req: Request|IRequest): asserts req is IRequest {
  if (!req?.user?.id) throw new Error('Request was not an IRequest');
}

คำตอบ #2

สมมติว่ามีการใช้อินเทอร์เฟซ IRequest สำหรับวัตถุประสงค์อื่นนอกเหนือจากข้างต้น สามารถทำตามแนวทางในบล็อกโพสต์นี้โดย Antonello Zanini: เพียงแค่ขยายอินเทอร์เฟซที่กำหนดโดย express โดยการเพิ่มไฟล์ประกาศ src/types/express/index.d.ts ด้วยเนื้อหาดังต่อไปนี้:

// src/types/express/index.d.ts

// to make the file a module and avoid the TypeScript error
export {}

declare global {
  namespace Express {
    export interface Request {
      user: {
        id: string;
      }
    }
  }
}

เช่นเดียวกับคำตอบอื่น ๆ กลไกการรวมการประกาศของ TypeScript จะรวมการประกาศทั้งสองเข้าด้วยกันเป็นหนึ่งเดียว

หวังว่าคำตอบช้านี้จะมีประโยชน์สำหรับผู้อ่านบางคน ฉันเพิ่งมีคำถามที่คล้ายกัน

บทความที่เกี่ยวข้อง