ฉันจะทำให้ Typescript เข้าใจว่าประเภทค่าของวัตถุที่มีชนิดเดียวกันคืออะไรโดยไม่เสียคีย์จากการประกาศแบบสแตติกได้อย่างไร?
ฉันจะทำให้ TypeScript เข้าใจประเภทค่าของอ็อบเจ็กต์ได้อย่างไรโดยไม่สูญเสียการเข้าถึงข้อมูลคีย์จากการประกาศแบบสถิตของมัน?
ในโค้ดด้านล่างนี้ฉันต้องการประโยชน์ของ
- TS รู้ชนิดของค่าด้วย ในกรณีนี้คือฟังก์ชัน ดังนั้นฉันไม่จำเป็นต้องระบุลายเซ็นของมันทุกครั้ง
- TS รู้คีย์ของอ็อบเจ็กต์จากการประกาศแบบสแตติก ดังนั้นฉันจึงสามารถอ้างอิงพวกมันในลักษณะที่ปลอดภัยตามชนิดจากที่อื่นได้
วัตถุนี้มีความเป็นเนื้อเดียวกันและจะมีฟังก์ชันจำนวนมาก การระบุลายเซ็นทุกครั้งดูเหมือนจะยุ่งยาก
ตัวอย่าง 1 - TS ไม่รู้คีย์ของออบเจ็กต์
interface Foo {
[key: string]: (n: number) => any
}
const obj1: Foo = {
// TS already knows that `n` is a number 🙂
bar(n) {return n}
}
type KeyOfObj1 = keyof typeof obj1
const kob1: KeyOfObj1 = "xyz" // This does not fail static type checking 😢
ตัวอย่าง 2 - TS ไม่ทราบลายเซ็นของฟังก์ชันซึ่งเป็นค่าของวัตถุ
const obj2 = {
// Notice here I have to specify the type of `n` 😢
bar(n: number) {return n}
}
type KeyOfObj2 = keyof typeof obj2
const kob2: KeyOfObj2 = "xyz" // This fails static type checking 🙂
ฉันจะได้สิ่งที่ดีที่สุดจากทั้งสองโลกได้อย่างไร?
ป.ล. ผมเข้าใจว่าผมสามารถดึงคีย์ออกมาทำเป็น enum ได้ แต่ดูเหมือนว่าจะน่าเกลียดและซ้ำซ้อนพอสมควร แต่อาจจะเป็นทางเดียวจริง ๆ 😕
คำตอบ #1
นี่เป็นสถานการณ์ที่ค่อนข้างพบได้บ่อย: คุณต้องการเขียนวัตถุ ให้สมาชิกของมันมีประเภทตามบริบทตามประเภทที่อ่อนกว่า (ในกรณีนี้คือ Foo) แต่ยังคงเก็บประเภทที่แข็งแรงกว่าไว้เพื่อวัตถุประสงค์อื่น (ในกรณีนี้ คุณไม่ต้องการที่จะ "ลืม" ว่า keys คืออะไร)
สิ่งที่มักทำกันคือการเขียนฟังก์ชันอัตลักษณ์ทั่วไป เช่น ด้านล่าง ซึ่งตรวจสอบว่าประเภทของอาร์กิวเมนต์ T เป็นซับไทป์ของ Foo ขณะที่คืนค่าประเภทที่เข้มงวดยิ่งกว่า T:
function foo<T extends Foo>(obj: T): T {
return obj;
}
จากนั้นเมื่อคุณสร้างวัตถุ คอมไพเลอร์จะกำหนดประเภทของมันตามบริบทเหมือนว่าเป็นซับไทป์บางประเภทของ Foo แต่ยังคงเก็บซับไทป์นั้น (พร้อมชื่อคีย์เฉพาะของมัน) สำหรับการกำหนดค่าให้กับ obj:
const obj = foo({
foo(n) { return n + 1; },
bar(n) { return n * 2; },
});
// 'foo' | 'bar'
type ObjKeys = keyof typeof obj
ลิงก์สนามเด็กเล่น
น่าเสียดาย ที่อย่างน้อยในตอนนี้ คุณจำเป็นต้องใช้ฟังก์ชันเอกลักษณ์ทั่วไปเพื่อให้สิ่งนี้ทำงานได้ ปัจจุบันมีข้อเสนอที่จะเพิ่มไวยากรณ์ใหม่ให้กับภาษา ซึ่งจะช่วยให้ปัญหาแบบนี้แก้ไขได้โดยไม่ต้องเพิ่มฟังก์ชันเสริมที่ "ไร้ประโยชน์"
