TypeScript - 基于参数类型的函数返回类型

时间:2021-07-26 14:42:55

标签: typescript

我有一些这样的接口:

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

1 个答案:

答案 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)来增加一些便利,但这会使示例更加复杂。