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

typescript - อย่าขยายชนิดลิเทอรัลที่เป็นไปได้ในลิเทอรัลของออบเจ็กต์


ฉันมักจะใช้วัตถุที่ไม่เปลี่ยนแปลงซึ่งประกอบด้วยสตริงและตัวเลข ตัวอย่างเช่น:

const InternalServerError = {
    status: 500,
    message: 'Internal Server Error'
}

ฉันไม่เคยเปลี่ยนคุณสมบัติ ดังนั้นฉันจึงอยากให้ประเภทของ InternalServerError ใช้ตัวเลขและสตริงลิตเทอรัลแบบนี้:

{
    status: 500,
    message: 'Internal Server Error'
}

อย่างไรก็ตาม TypeScript จะขยายประเภทในประเภทที่สืบเนื่องมาจาก InternalServerError โดยอัตโนมัติ:

{
    status: number,
    message: string
}

ฉันสามารถแปลงคุณสมบัติแต่ละอย่างเป็นประเภทตัวอักษรของมันได้ อย่างนี้:

const InternalServerError = {
    status: 500 as 500,
    message: 'Internal Server Error' as 'Internal Server Error'
}

แต่นี่มีปัญหาสองข้อ:

  1. การทำซ้ำ โดยเฉพาะสำหรับสตริงยาว
  2. สามารถแปลงค่ากับชนิดลิติรัลที่ผิดได้ (500 as 501 เป็นตัวอย่าง) และตัวคอมไพเลอร์จะไม่รายงานข้อผิดพลาด ทำให้สิ่งนี้มีความเสี่ยงต่อการพิมพ์ผิด

วิธีที่ง่ายที่สุด (มีโค้ดซ้ำหรือโค้ดพื้นฐานน้อยที่สุด) และปลอดภัยต่อชนิดข้อมูลมากที่สุด (มีความเป็นไปได้ที่ข้อผิดพลาดชนิดข้อมูลที่ไม่ได้จับได้น้อยที่สุด) ในการทำให้สิ่งนี้เกิดขึ้นคืออะไร?

คำตอบ #1

คุณสามารถสร้างฟังก์ชันช่วยเหลือพร้อมกับเจเนอริกเพื่อประมาณประเภทที่แม่นยำ (500 แทน number เป็นตัวอย่าง) :

function makeError<
    S extends number,
    M extends string
>(status: S, message: M): {status: S, message: M} {
    return {status, message}
}

const InternalServerError = makeError(500, 'Internal Server Error')
const NotFoundError = makeError(404, 'Not found')

// Code = 500
type Code = (typeof InternalServerError)['status']
// Message = Internal Server Error
type Message = (typeof InternalServerError)['message']