คอมไพเลอร์ typescript infering type กับ generics

Oct 01 2020

ฉันพยายามบรรลุisEventOfTypeฟังก์ชันทั่วไป ในสถานที่หลายเหตุการณ์ที่เกิดขึ้นจาก StateMachine x รัฐมีการกำหนด (ดูMaschine1Events, Maschine2Events) เมื่อประมวลผลเหตุการณ์ x-state สามารถระบุได้เพียงMaschine1EventsหรือMaschine2Eventsแต่ไม่สามารถบอกได้ว่าเหตุการณ์ใดถูกประมวลผล

ฉันจึงต้องการวิธีตรวจสอบว่าเหตุการณ์เป็นประเภทที่ฉันคาดหวังหรือไม่ ตัวอย่างเช่นเหตุการณ์ "CHANGE" มีให้

ตัวอย่างนี้ใช้งานได้อย่างสมบูรณ์ แต่สิ่งที่ฉันไม่เข้าใจคือเหตุใดในfWithoutGenericฟังก์ชันจึงไม่สามารถอนุมานประเภทได้

    export type Event<TType, TData = unknown> = { type: TType } & TData;
    export type Value<TType> = { value: TType };

    type Maschine1Events = Event<'INC'> | Event<'DEC'> | Event<'CHANGE', Value<number>>;
    type Maschine2Events = Event<'INC', Value<number> | Event<'SEND_MESSAGE', Value<string>>;

    type EventTypes<T extends Event<unknown>> = T['type'];
    type EventOfType<T extends Event<unknown>, U extends EventTypes<T> = EventTypes<T>> = Extract<T, { type: U }>;
    function isEventOfType<T extends Event<unknown>, U extends EventTypes<T> = EventTypes<T>>(
        event: T,
        type: U
    ): event is EventOfType<T, U> {
        return event.type === type;
    }

    function fWithGenerics(event: Maschine1Events) {
        if (isEventOfType<Maschine1Events, 'CHANGE'>(event, 'CHANGE')) {
            const a: Event<'CHANGE', Value<number>> = event;
        }
    }
    function fWithOutGenerics(event: Maschine2Events) {
        if (isEventOfType(event, 'SEND_MESSAGE')) {
            const a: Event<'SEND_MESSAGE', Value<string>> = event; //Why can't this be infered
        }
    }

ลิงก์สนามเด็กเล่นไปยังรหัส

คำตอบ

4 jcalz Oct 01 2020 at 01:33

ฉันมีแนวโน้มที่จะให้isEventOfType()ลายเซ็นต่อไปนี้:

function isEventOfType<E extends { type: string }, T extends E['type']>(
    event: E,
    type: T
): event is Extract<E, { type: T }> {
    return event.type === type;
}

ฉันทำให้พารามิเตอร์ง่ายขึ้นเล็กน้อยและลบค่าเริ่มต้นของพารามิเตอร์ทั่วไปสำหรับพารามิเตอร์ที่สอง ฉันไม่รู้ว่าประเด็นเหล่านั้นคืออะไร แต่ถ้าคุณไม่ต้องการมันก็อาจเป็นเพียงสิ่งที่ทำให้ไขว้เขว

การเปลี่ยนแปลงใหญ่ที่นี่เป็นที่Eประเภทของeventพารามิเตอร์ในขณะนี้มีข้อ จำกัดในการแทน{type: string} ให้เรียบเรียงคำใบ้ที่จะอนุมานชนิดสตริงตัวอักษรสำหรับชนิดของพารามิเตอร์ (ดูMicrosoft / typescript # 10676สำหรับรายละเอียดเกี่ยวกับเกี่ยวกับการอนุมานชนิดที่แท้จริงเกิดขึ้น){type: unknown}stringTtype

ตอนนี้รหัสทำงานได้ตามที่คุณคาดหวังฉันคิดว่า:

function m1(event: Maschine1Events) {
    if (isEventOfType(event, 'CHANGE')) {
        event.value.toFixed(2); // okay
    } else {
        event // {type: "INC"} | {type: "DEC"}
    }
}
function m2(event: Maschine2Events) {
    if (isEventOfType(event, 'SEND_MESSAGE')) {
        event.value.toUpperCase(); // okay
    } else {
        event.value.toFixed(2); // okay
    }
}

ลิงก์สนามเด็กเล่นไปยังรหัส