好的,这个有点难以解释。我的示例令人费解,但我尝试在保留错误的同时尽可能简化它。
type Handler = (...args: any[]) => void;
const handlers: { [eventName: string]: Handler } = {};
type Events = {
// Map event signature (arg types) to event name
[eventName: string]: any[];
};
function createOnEvent<UserEvents extends Events>() {
type ValidEventName = Extract<keyof UserEvents, string>;
return <EventName extends ValidEventName>(
eventName: EventName,
eventHandler: (...args: UserEvents[EventName]) => void,
) => {
handlers[eventName] = eventHandler; // [0] ERROR HERE
};
}
[0]类型“ any []”不能分配给类型“ UserEvents [EventName]”
两件事使我感到困惑:
UserEvents[EventName]
应该等于any[]
UserEvents[EventName]
分配给any[]
,而不是相反... 顺便说一句,此API的用法如下:
type FooEvents = {
eventOne: [number];
eventTwo: [string, boolean];
};
const onEvent = createOnEvent<FooEvents>();
onEvent('eventOne', (arg1: number) => null); // OK
onEvent('eventTwo', (arg1: string, arg2: boolean) => null); // OK
onEvent('eventOne', (arg1: string) => null); // error
按预期工作。唯一失败的部分是当我要将处理程序存储在handlers
中时,该处理程序应具有包含任何args列表的函数。
我意识到这有点令人困惑,所以让我知道我是否可以澄清任何事情。我很高兴深入浅出。谢谢!
答案 0 :(得分:3)
原因
您遇到的错误源于使用--strictFunctionTypes
标志时函数类型为contravariant in their argument type的事实。
那是什么意思?这意味着当函数期望其参数使用某种类型时,允许您使用比该期望类型更具体或更广泛的参数。
在您的情况下,handlers
是通用处理程序的字典。这些处理程序需要any[]
参数。他们的定义也可以这样写:
const handlers: {
[eventName: string]: (...args: any[]) => void
} = {};
但是,您的eventHandler
不仅仅是通用处理程序。它不仅接受any[]
个参数,还接受UserEvents
描述的一些非常具体的参数。这与矛盾的想法背道而驰-传递给函数的参数不得比其签名所要求的参数更具体。换句话说,typeof eventHandler
无法分配给Handler
。
您可以做什么
使用类型断言在本地扩展eventHandler
的类型:
handlers[eventName] = eventHandler as Handler;
使编译器接受处理程序的特定定义:
type Handler<T extends any[] = any> = (...args: T) => void;