การยืนยันประเภททั่วไปของ Typescript
ดังนั้นนี่คือสรุปของการสังเกตของฉันจากการทำงานกับ TypeScript
นี่คือโค้ดบางส่วน:
type someTypeEnum = '1';
type someOtherTypeEnum = '2' | '3';
type combinedTypeEnum = someTypeEnum | someOtherTypeEnum;
นี่คือตัวอย่างกรณีแรก :-
function typeAssertion<T extends combinedTypeEnum>(args: T): args is someTypeEnum {
// The error i get
// A type predicate's type must be assignable to its parameter's type.
// Type '"1"' is not assignable to type 'T'.
return undefined;
}
ฉันไม่เข้าใจว่าทำไมสิ่งนี้ถึงล้มเหลว เพราะเราจำกัดอาร์กิวเมนต์ของเราไว้ที่ combinedTypeEnum แล้ว ในกรณีที่เราทำ
typeAssertion('4')
เราก็ได้รับข้อผิดพลาดที่ระบุว่า '4' ไม่ใช่อาร์กิวเมนต์ที่ถูกต้องแล้ว ดังนั้นทำไม args is someTypeEnum ถึงถูกพิจารณาว่าเป็นเงื่อนไขที่ไม่ถูกต้อง
นี่คือกรณีที่สอง :-
function typeAssertion(args: combinedTypeEnum): args is someTypeEnum {
return undefined;
}
นี่ดูเหมือนจะใช้ได้ดี แต่ในกรณีที่เราทำแบบนี้ :-
function someFunction<T extends combinedTypeEnum>(args: T): T {
if (typeAssertion(args)) {
// args here is 'T & "1"'
args
}
return args
};
ทำไมเราถึงมี T และ "1" ไม่ใช่แค่ "1" เราได้ระบุอย่างชัดเจนว่ามันคือ someTypeEnum
ฉันอยากรู้อย่างมากว่าทำไมจึงมีการตัดสินใจเช่นนั้น มันจะเป็นประโยชน์มากที่จะได้เห็นว่าอะไรพังทลายอย่างไรในกรณีที่สิ่งต่าง ๆ ถูกทำในวิธีที่แตกต่างกัน
คำตอบ #1
extends ไม่ค่อยมีความหมายมากนักเมื่อคุณมีสตริงลิตเทอรัล เพื่อให้อธิบายได้ง่ายขึ้น ให้ฉันใช้ชนิดข้อมูลอื่น พิจารณาคลาสทั้งสามนี้:
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
เมื่อเราใช้เจเนอริก ประเภทจริงจะถูกกำหนดโดยผู้เรียก:
function foo<T extends Animal>(arg: T) {}
foo(new Dog()); //T is Dog, equivalent to foo(arg: Dog) {}
foo(new Cat()); //T is Cat, equivalent to foo(arg: Cat) {}
ตอนนี้คุณอาจเริ่มเห็นแล้วว่าเรากำลังจะไปทางไหน ลองใช้ตัวกำหนดประเภท:
function foo<T extends Animal>(arg: T): arg is Cat {}
เมื่อเราส่งค่า foo(new Dog()) ตัวอย่างล่าสุดจะเทียบเท่ากับนี้:
function foo(arg: Dog): arg is Cat {}
และแน่นอนว่ามันไม่ทำงานหรือสมเหตุสมผล
ส่วนตัวอย่างที่สองของคุณ: ประเภทของตัวแปรไม่เปลี่ยน จุดสำคัญคือการยืนยันประเภทที่เฉพาะเจาะจงจะทำให้คอมไพเลอร์อนุญาตให้คุณทำสิ่งใด ๆ ที่สามารถทำได้กับประเภทนี้
คำตอบ #2
อัปเดต :
หรือง่ายกว่านี้:
function typeAssertion(args: combinedTypeEnum): args is someTypeEnum {
return args === "1";
}
ดูสนามเด็กเล่นนี้
ต้นฉบับ :
เรื่องนี้ทำให้ฉันงงไปนานเหมือนกัน จริง ๆ แล้ววิธีแก้ปัญหา (อย่างน้อยในปี 2021 ไม่แน่ใจว่าเป็นตอนที่มีการถามคำถามนี้หรือไม่) คือ:
function typeAssertion<T extends combinedTypeEnum>(args: T): args is T & someTypeEnum {
return args === "1";
}
แนวคิดเบื้องหลังสิ่งนี้ (เท่าที่ฉันเข้าใจจากคำตอบนี้) คือ: เมื่อคุณเรียก typeAssertion("2") ค่าของ T จะเป็น "2" (ประเภทตัวอักษร "2") ซึ่งหมายความว่าคุณจะได้ฟังก์ชันดังนี้:
function typeAssertion(args: "2"): args is someTypeEnum
ซึ่งชัดเจนว่าไม่มีเหตุผล ฉันไม่แน่ใจว่าวิธีแก้ปัญหา (ด้วย T &) จะสมเหตุสมผลมากกว่านี้หรือไม่ แต่ก็ใช้ได้:
type someTypeEnum = '1';
type someOtherTypeEnum = '2' | '3';
type combinedTypeEnum = someTypeEnum | someOtherTypeEnum;
function typeAssertion<T extends combinedTypeEnum>(args: T): args is T & someTypeEnum {
return args === "1";
}
const a: combinedTypeEnum = "1"
const b: combinedTypeEnum = "2"
const c: combinedTypeEnum = "3"
const d = "1"
const e = "2"
const f = "4"
let one: "1" = "1"
if (typeAssertion(a)) one = a
if (typeAssertion(b)) one = b
if (typeAssertion(c)) one = c
if (typeAssertion(d)) one = d
if (typeAssertion(e)) one = e
if (typeAssertion(f)) one = f // this one gives an error
ดูในสนามเด็กเล่น
