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

การยืนยันประเภททั่วไปของ 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

ดูในสนามเด็กเล่น

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