[英]Get type of generic's value by key declared on a property in Typescript interface and use it as the type of a function's parameter
[英]Typescript use generics to get type-value from interface using type-key parameter?
我試圖在 Typescript 類型上做得更好,並且需要一個類型安全的事件發射器。 我現在嘗試了許多不同的方法,但我似乎無法正確解決類型。 你能看出我哪里出錯了嗎?
在下面的示例中,我有一個“事件”類型,它將事件名稱映射到必須與該事件一起傳遞的 arguments。 因此,如果我發出“Foo”,我還必須傳遞一個“bar”字符串,並且偵聽器應該知道有一個“bar”屬性要讀取。
interface Events {
Foo: {
bar: string;
}
}
type EventKeys = keyof Events
class Emitter {
...
emit<K extends EventKeys> (title: K, value: Events[K]): void {
// With this signature I want to require if the caller specifies a title of "Foo"
// then they must specify value as "{bar: string}". This part looks to work great!
this.emitter.emit("connection", [title, value])
}
public on (listener: any): void {
// I use "any" here because this part of the code isn't super relevant to this example
this.emitter.on('connection', listener.event.bind(f))
}
}
class Listener {
...
event<K extends EventKeys> ([title, value]: [title: K, value: Events[K]]): void {
switch(title) {
case "Foo":
console.log(value)
// Here "value" is of type "Events[K]",
// which I take to mean it should know it's type "Events[Foo]"
// or actually "{bar: string}",
// but I don't get the autocompletion I expect.
break
}
}
}
是否不可能像Events[K]
一樣從 generics 獲得{bar: string}
?
這里有一些你想要的東西。 emit()
現在是類型安全的,您可以按 Tab 鍵自動完成可能的事件名稱。 需要 TypeScript 4+ 。
演示: https://repl.it/@chvolkmann/Typed-EventEmitter
import { EventEmitter } from 'events'
interface EventTree {
Connected: {
foo: string;
};
}
type EventName = keyof EventTree;
type FormattedEvent<E extends EventName> = [E, EventTree[E]];
class MyListener {
handleEvent<E extends EventName>([event, args]: FormattedEvent<E>) {
console.log(`Event "${event}" happened with args`, args);
}
}
class MyEmitter {
protected emitter: EventEmitter
constructor() {
this.emitter = new EventEmitter()
}
emit<E extends EventName>(name: E, args: EventTree[E]) {
this.emitter.emit('connection', [name, args]);
}
registerListener(listener: MyListener) {
// The typing here is a bit overkill, but for demostration purposes:
const handler = <E extends EventName>(fEvent: FormattedEvent<E>) => listener.handleEvent(fEvent)
this.emitter.on('connection', handler)
}
}
const emitter = new MyEmitter()
emitter.registerListener(new MyListener())
// This supports autocompletion
// Try emitter.emit(<TAB>
emitter.emit('Connected', { foo: 'bar' })
// TypeScript won't compile these examples which have invalid types
// > TS2345: Argument of type '"something else"' is not assignable to parameter of type '"Connected"'.
// emitter.emit('something else', { foo: 'bar' })
// > TS2345: Argument of type '{ wrong: string; }' is not assignable to parameter of type '{ foo: string; }'.
// emitter.emit('Connected', { wrong: 'type' })
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.