typescript 具有给定类型的事件的有效负载类型推断

k2arahey  于 2022-11-18  发布在  TypeScript
关注(0)|答案(2)|浏览(118)

我有一个场景,其中有如下所示的不同事件:

type EventType = 'foo' | 'bar'

type Event = {
  type: 'foo',
  timestamp: number,
  payload: { a: number }
} | {
  type: 'bar',
  timestamp: number,
  payload: { b: string }
}

然后我有一个这样的听众:

on('foo', event => {
  // Here we should know that the type must be 'foo',
  // and therefore the payload has the shape {a: number}
  // but I don't know how to tell that to TS
})

我尝试了一些不同的方法,但到目前为止,我所做的一切都是让编译器停止编译🥲
我想this question可以帮上忙,但是我没能让它工作。我想问题是我使用了一个字面联合而不是枚举。
我想这是一个发生在许多地方的情况,所以我希望能更容易地找到解决办法。

gk7wooem

gk7wooem1#

我的建议是将on()作为generic函数,其第一个参数的类型为K,然后根据该类型编写event回调参数的类型。
为此,我将首先编写一个名为EventMap的帮助器类型,如下所示:

type EventMap = { [E in Event as E['type']]: E };

它会将Event重新Map至对象型别,其索引键是on()的预期第一个参数,而其值是event回呼参数的型别,如下所示:

/* type EventMap = {
  foo: {
    type: 'foo';
    timestamp: number;
    payload: {
      a: number;
    };
  };
  bar: {
    type: 'bar';
    timestamp: number;
    payload: {
      b: string;
    };
  };
} */

使用该类型,on()的调用签名可以写为:

declare function on<K extends keyof EventMap>(
  type: K,
  cb: (event: EventMap[K]) => void
): void;

因此,event回调参数属于indexed access typeEventMap[K],它是EventMap的值类型,位于键K处。
让我们来测试一下:

on('foo', event => {
  event.payload.a.toFixed(2); // okay
});

on('bar', event => {
  event.payload.b.toUpperCase(); // okay
});

看起来不错!
Playground代码链接

ecfdbz9o

ecfdbz9o2#

if (event.type == 'foo') {
   // event is Extract<event, {type: 'foo'}>
   event.payload.a
} else {
   // event is Exclude<event, {type: 'foo'}>
   event.payload.b
}

相关问题