Typescript编译器使用泛型推断类型

时间:2020-09-30 18:09:41

标签: typescript

我尝试实现通用的isEventOfType功能。在多个地方,定义了来自x状态状态机的事件(请参见Maschine1EventsMaschine2Events)。在处理事件时,x状态只能提供Maschine1EventsMaschine2Events,而不能确切地说明处理了哪个事件。

因此,我需要一种方法来检查事件是否为我期望的类型。例如,提供了“ 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
        }
    }

Playground link to code

1 个答案:

答案 0 :(得分:4)

我倾向于给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;
}

我稍微简化了参数,并删除了第二个参数的generic parameter defaults。我不知道这些是什么意思,但是除非您需要它们,否则它们可能只会分散您的注意力。

此处最大的变化是,E参数的event类型现在是constrained{type: string}而不是{type: unknown}string为编译器提供了一个提示,可以推断出T参数的类型type的{​​{3}}(有关如何推断文字的详细信息,请参见string literal type)。类型发生)。

现在,我认为代码可以按预期工作:

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
    }
}

microsoft/TypeScript#10676