带有查找对象的打字稿中的歧视类型工会

发布于 2025-02-13 19:33:49 字数 2448 浏览 2 评论 0 原文

我有一些可能具有不同类型签名的事件接口,具体取决于它是什么类型的事件。假设我们有“ fizzchange”和“ buzzchange”,并且

interface EventTypeBase {
  id: string;
  timestamp: string;
}

interface FizzChange extends EventTypeBase {
  name: 'fizz';
  payload: string;
}

interface BuzzChange extends EventTypeBase {
  name: 'buzz';
  payload: number;
}

现在我们定义了它,它们都扩展了“ eventTypebase”,我们可以将其用于函数,并且有条件的语句检查我们正在尝试使用的是什么

const mapEventsWithIfs = (event: EventType) => {
  // This works, typescript can see what type we want to use
  if (event.name === 'fizz') {
    return event.payload; // Typescript can see that this is a string
  }

  if (event.name === 'buzz') {
    return event.payload; // And this is a number
  }

  return;
}

类型,是,我想使用一个查找对象,而每种类型都具有自己的处理程序功能,但这是不起作用的,而不是IF语句。如何以一种可以工作的方式键入查找对象?

这是重现它的链接

const mapEvents = (event: EventType) => {
  // This doesn't work, event gets reduces to never
  // Argument of type 'EventType' is not assignable to parameter of type 'never'.
  //   The intersection 'FizzChange & BuzzChange' was reduced to 'never'
  //   because property 'name' has conflicting types in some constituents.
  //     Type 'FizzChange' is not assignable to type 'never'.(2345)
  return EventHandlersMap[event.name](event);
}

I have some event interface that might have different type signature, depending on what type of event it is. Let's say we have 'FizzChange' and 'BuzzChange', and they both extend 'EventTypeBase'

interface EventTypeBase {
  id: string;
  timestamp: string;
}

interface FizzChange extends EventTypeBase {
  name: 'fizz';
  payload: string;
}

interface BuzzChange extends EventTypeBase {
  name: 'buzz';
  payload: number;
}

Now that we defined it, we can use it in function, and with conditional statement check what type we are working with

const mapEventsWithIfs = (event: EventType) => {
  // This works, typescript can see what type we want to use
  if (event.name === 'fizz') {
    return event.payload; // Typescript can see that this is a string
  }

  if (event.name === 'buzz') {
    return event.payload; // And this is a number
  }

  return;
}

What i'm trying to achieve, is that instead of if statements, I want to use a lookup object, where each type gets its own handler function, but that doesn't work. How can I type lookup object, in a way that will work?

Here is a link to reproduce it

const mapEvents = (event: EventType) => {
  // This doesn't work, event gets reduces to never
  // Argument of type 'EventType' is not assignable to parameter of type 'never'.
  //   The intersection 'FizzChange & BuzzChange' was reduced to 'never'
  //   because property 'name' has conflicting types in some constituents.
  //     Type 'FizzChange' is not assignable to type 'never'.(2345)
  return EventHandlersMap[event.name](event);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一世旳自豪 2025-02-20 19:33:49

Here's a somewhat involved approach that uses the 歧视联盟 映射类型href =“ https://stackoverflow.com/a/48751007/3558960”>在这里#generic-constraints“ rel =“ nofollow noreferrer”> generic约束

interface EventTypeBase {
  id: string;
  timestamp: string;
}

interface FizzChange extends EventTypeBase {
  name: 'fizz';
  payload: string;
}

interface BuzzChange extends EventTypeBase {
  name: 'buzz';
  payload: number;
}

type DiscriminateUnion<T, K extends keyof T, V extends T[K]>
  = T extends Record<K, V> ? T : never;

type MapDiscriminatedUnion<T extends Record<K, string>, K extends keyof T>
  = { [V in T[K]]: DiscriminateUnion<T, K, V> };

type EventType = FizzChange | BuzzChange;

type EventMap = MapDiscriminatedUnion<EventType, 'name'>;

const eventHandlersMap: {
  [K in keyof EventMap]: (event: EventMap[K]) => EventMap[K]['payload'];
} = {
  'fizz': (event: FizzChange) => event.payload,
  'buzz': (event: BuzzChange) => event.payload
}

const mapEvents = <K extends keyof EventMap>(event: EventMap[K]) => {
  const name: K = event.name;
  return eventHandlersMap[name](event);
}

游乐场链接

Here's a somewhat involved approach that uses the discriminated union mapped type approach described here along with a generic constraint:

interface EventTypeBase {
  id: string;
  timestamp: string;
}

interface FizzChange extends EventTypeBase {
  name: 'fizz';
  payload: string;
}

interface BuzzChange extends EventTypeBase {
  name: 'buzz';
  payload: number;
}

type DiscriminateUnion<T, K extends keyof T, V extends T[K]>
  = T extends Record<K, V> ? T : never;

type MapDiscriminatedUnion<T extends Record<K, string>, K extends keyof T>
  = { [V in T[K]]: DiscriminateUnion<T, K, V> };

type EventType = FizzChange | BuzzChange;

type EventMap = MapDiscriminatedUnion<EventType, 'name'>;

const eventHandlersMap: {
  [K in keyof EventMap]: (event: EventMap[K]) => EventMap[K]['payload'];
} = {
  'fizz': (event: FizzChange) => event.payload,
  'buzz': (event: BuzzChange) => event.payload
}

const mapEvents = <K extends keyof EventMap>(event: EventMap[K]) => {
  const name: K = event.name;
  return eventHandlersMap[name](event);
}

Playground link

终陌 2025-02-20 19:33:49

添加 Fizz Buzz 作为类型。

type ActionType = 'fizz' | 'buzz';

在EvensHandLersMap中,将您的对象类型类型。因此,打字稿将了解对象。

const EventHandlersMap: { [K in ActionType]: (event: ExampleEvent & any) => void } = {
    fizz: (event: FizzChange) => event.payload,
    buzz: (event: BuzzChange) => event.payload
}

Add fizz and buzz as type.

type ActionType = 'fizz' | 'buzz';

In EventHandlersMap give type to your object. So typescript will understand the object.

const EventHandlersMap: { [K in ActionType]: (event: ExampleEvent & any) => void } = {
    fizz: (event: FizzChange) => event.payload,
    buzz: (event: BuzzChange) => event.payload
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文