这是一个事件发射器的设计:
n
设计有效,但有两件事我不明白:
type EventMap = Record<string, any>;
type EventKey<T extends EventMap> = string & keyof T;
type EventReceiver<T> = (params: T) => void;
interface Emitter<T extends EventMap> {
on<K extends EventKey<T>>
(eventKey: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>
(eventKey: K, params: T[K]): void;
}
// example usage
const emitter = eventEmitter<{
data: Buffer | string;
end: undefined;
}>();
// OK!
emitter.on('data', (x: string) => {});
// Error! 'string' is not assignable to 'number'
emitter.on('data', (x: number) => {});
:为什么要在这里加一个EventKey
?
为什么我们需要带有 string &
和 K
方法的泛型 on
,emit
参数的类型不能简单地为 eventKey
?
非常感谢。
答案 0 :(得分:2)
EventKey:为什么要在这里加一个字符串&?
交集 string & ...
用于确保只有实际类型为 string
的键才能用于定义 EventKey
类型。
即使 EventMap
定义为 Record<string,any>
,您仍然可以为其分配具有非字符串属性的对象:
const a: EventMap = {
foo: 'bar',
100: 'baz', // whoops, number property
}
为什么?因为对象可以有 excess properties。
因此在这种情况下,对于非 string & keyof T
类型的流氓属性,never
将导致 string
。
eventKey 参数的类型不能简单地为 keyof T 吗?
这是为了允许定义为联合类型的键,它确实扩展了 string
(另见 this answer):
type EnumK = 'alpha' | 'beta'
type MyObject = {[P in EnumK]: string }
const obj: SomeObj = {
alpha: 'foo',
beta: 'bar'
}
这里obj
的键类型是string
的扩展,即字符串文字类型alpha
和{{1}的联合}.因此,通过使用 beta
,您可以捕获这种关系并确保类型索引 K extends ...
中的类型安全。