如何在TypeScript中定义由事件处理程序名称和事件处理程序组成的类型

时间:2018-05-08 07:34:33

标签: javascript reactjs typescript

我想使用React和TypeScript定义一个包含一对事件处理程序名称和事件处理程序的对象,这样我就可以传递一个对象数组,如

{
    eventHandlerName: 'onBlur',
    eventHandler: (e: React.FocusEvent<HTMLInputElement>) => { /* something */},
}

我尝试将此类型定义为

type SpecifiedEventHandler<I, E extends keyof React.DOMAttributes<I>> = {
    eventHandlerName: E,
    handlerFunction: React.DOMAttributes<I>[E];
};

以便称之为

const myObject: SpecifiedEventHandler<HTMLInputElement, keyof React.DOMAttributes<HTMLInputElement>>[] = {
    [
        {
            eventHandlerName: 'onBlur',
            handlerFunction: (e: React.FocusEvent<HTMLInputElement>) => { /* something */},
        },
        /* more here */
    ]
};

但问题是这不够严格。我可以将'foobar'作为handlerFunction传递,它编译得很好。

我在这里缺少什么?关键是我希望handlerFunction具有React.DOMAttributes<HTMLInputElement>中与eventHandlerName名称对应的字段类型。

谢谢!

1 个答案:

答案 0 :(得分:1)

问题是数组元素的类型是SpecifiedEventHandler<HTMLInputElement, keyof React.DOMAttributes<HTMLInputElement>>,意味着E将是keyof React.DOMAttributes<HTMLInputElement>>所以handlerFunction将是React.DOMAttributes<HTMLInputElement>[keyof React.DOMAttributes<HTMLInputElement>>]所以基本上 ANY 值,可以是React.DOMAttributes<HTMLInputElement>属性的值,包含{}等很多类型,因此任何类型都与handlerFunction兼容。

如果为E指定更具限制性的类型,则会得到所需的错误,但可能不是具有多个事件类型数组的所需功能:

const myObject: SpecifiedEventHandler<HTMLInputElement, 'onBlur'>[] =
    [
        {
            eventHandlerName: 'onBlur',
            handlerFunction: '(e: React.FocusEvent<HTMLInputElement>) => { /* something */},'
        },
    ]; // Error
const myObjectOk: SpecifiedEventHandler<HTMLInputElement, 'onBlur'>[] =
    [
        {
            eventHandlerName: 'onBlur',
            handlerFunction: (e: React.FocusEvent<HTMLInputElement>) => { /* something */},
        },
    ]; // Ok

您可以创建一个辅助函数来验证数组中的每个条目,您需要为每个项目手动调用它:

function event<I, E  extends keyof React.DOMAttributes<I>>(event: SpecifiedEventHandler<I, E>) : typeof event {
    return event;
}

const myObject: SpecifiedEventHandler<HTMLInputElement, keyof React.DOMAttributes<HTMLInputElement>>[] =
    [
        // Fun trick I is inferred based on return type 
        event({ 
            eventHandlerName: "onBlur",
            handlerFunction: (e: React.FocusEvent<HTMLInputElement>) => { /* something */},
        }),
        // Error eventHandlerName not ok 
        event({ 
            eventHandlerName: "onBlur2",
            handlerFunction: (e: React.FocusEvent<HTMLInputElement>) => { /* something */},
        }),
        // Error handlerFunction not ok 
        event({ 
            eventHandlerName: "onBlur",
            handlerFunction: '(e: React.FocusEvent<HTMLInputElement>) => { /* something */}',
        }),
    ];