我正在努力解决如何强输入我的消息广播类
基本上我想发送和收听一组有限的消息,但似乎不可能输入解决方案的设计方式:
代码:
interface Messages {
'navigation.start': {
path: string
},
'navigation.error': {
path: string,
reason: 404 | 403
},
'auth.loggedin' : {
user: string,
}
}
type AvailableMessages = keyof Messages;
type Listeners = {
[K in AvailableMessages]: Array<(payload: Messages[K]) => void>
}
class Messaging {
private listeners: Listeners = {
'navigation.start': [],
'navigation.error': [],
'auth.loggedin': [],
};
public listen<K extends AvailableMessages, M = Messages[K]>(message: K, callback: (payload: M) => void) {
this.listeners[message].push(callback);
}
public send<K extends AvailableMessages, M = Messages[K]>(message: K, payload: M) {
const messageListeners = this.listeners[message];
if(messageListeners == null) {
return;
}
messageListeners.forEach(l => l(payload));
}
}
new Messaging().listen('navigation.start', () => { console.log('?') });
new Messaging().listen('navigation.start', (a) => { console.log(a.path) });
new Messaging().listen('navigation.error', (a) => { console.log(a.reason) });
new Messaging().listen('auth.loggedin', (a) => { console.log(a.user) });
答案 0 :(得分:0)
方法签名有两个问题
public listen<K extends AvailableMessages, M = Messages[K]>(
message: K,
callback: (payload: M) => void
)
首先,M
的类型没有限制,所以我可以做
new Messaging().listen<'navigation.start', number>('navigation.start', 3)
这不是类型安全的。
其次,即使删除了类型参数 M
(即 callback
的类型为 (payload: Messages[K]) => void
)
new Messaging()
.listen<'navigation.start' | 'navigation.error'>(
'navigation.start',
({ reason }) => console.log("navigation.error listener called")
)
这会违反类型安全。
理想情况下,可以通过某种方式指定泛型参数恰好是键之一(即没有联合类型)。据我所知,在 TypeScript 中并没有真正做到这一点的好方法。
可以通过滥用分配条件类型来创建将联合类型转换为 never
的类型:
type SingleHelper<T, V> = T extends any
? [V] extends [T]
? V
: never
: never;
type Single<T> = SingleHelper<T, T>
使用这种类型,Single<"hello">
的计算结果为 "hello"
,但 Single<"hello" | "goodbye">
的计算结果为 never
。
然而,即使我们在 listen
的签名中使用了这种类型,即如果我们写了
public listen<K extends AvailableMessages>(
message: Single<K>,
callback: (payload: Messages[Single<K>]) => void
)
TypeScript 无法推断该类型仅包含确切的键。虽然您仍然会在正文中遇到类型错误,但它会迫使调用者正确使用该函数。