我有一些这样的接口:
export interface BroadcastChannel {
name: string;
payload: Record<string, unknown>;
}
export interface TaskWindowCloseChannel extends BroadcastChannel {
name: 'TASK_WINDOW_CLOSE';
payload: {
taskId: number;
};
}
export interface WindowFocusChannel extends BroadcastChannel {
name: 'WINDOW_FOCUS';
payload: {
windowName: string;
};
}
将在函数中使用:
public listenChannel<T extends BroadcastChannel>(name: T['name']): Observable<T> {
const obs = new Subject<T>();
const bc = new BroadcastChannel(name);
bc.onmessage = (ev) => obs.next(ev.data);
return obs.asObservable();
}
我想在这个函数上有一个类型逻辑,每当我在以前的接口中放置 'TASK_WINDOW_CLOSE'
或 name
属性的任何其他值时,我都会输入函数的返回类型observable 中正确的 payload
属性类型。
而且我想避免过载,因为将来会有很多类似 TaskWindowChannel 的接口。
所以理想的输出是:
listenChannel('TASK_WINDOW_CLOSE').subscribe((data: { taskId: number }) => {}); // data type is inferred by name param
listenChannel('WINDOW_FOCUS').subscribe((data: { windowName: string }) => {}); // data type is inferred by name param
答案 0 :(得分:0)
你不能直接实现这个。但是,您可以使用一个接口来“链接”您的有效负载和您的姓名
interface TaskWindowClosePayload{
taskId: number,
}
interface WindowFocusPayload {
windowName: string,
}
interface MapPayloadNameToType {
'TASK_WINDOW_CLOSE': TaskWindowClosePayload,
'WINDOW_FOCUS': WindowFocusPayload,
};
interface MyBroadcastChannel<T extends keyof MapPayloadNameToType> {
name: T;
payload: MapPayloadNameToType[T];
}
现在您可以使用 const 字符串作为参数:
function foo<T extends keyof MapPayloadNameToType>(name: T): MyBroadcastChannel<T> {
return null as any; // implement and make sure this is correct at runtime!
}
foo('TASK_WINDOW_CLOSE').payload.taskId;
这个想法是有一个类型,其中属性是您的频道名称,属性的类型是该频道的有效负载的类型。然后可以使用 keyof
关键字将这些键作为联合类型获取。
另请参阅 jcalz 的 the comment above,他走相反的路线:从联合类型构建名称-类型映射类型。
您可以通过引入更多类型(如 type ChannelNames = keyof MapPayloadNameToType
)来增加一些便利,但这会使示例更加复杂。