简体   繁体   English

function 类型可以接受相交类型作为 TypeScript 中的 arguments 吗?

[英]Can a function type accept intersected types as arguments in TypeScript?

Say I've got these types defined:假设我已经定义了这些类型:

export type Event = {
  name: EventName,
  timestamp: Timestamp,
};

export type EventWithMetadata = Event & {
  deviceId: ID,
};

I'm looking to define a common function interface like so:我希望像这样定义一个通用的 function 接口:

export type SideEffect = (event: Event, run?: Run) => Partial<Run>;

and using it with this function并将其与此 function 一起使用

// Note: many of these side-effect functions exist, with multiple specializations of the base Event
// type as potential arguments. This is just one example.
const updateTiming: SideEffect = (event: EventWithMetadata, run) => {
  log.debug('updateTiming: %j', event);
  return {
    lastUpdated: event.timestamp,
    deviceId: event.deviceId,
  };
};

but then I hit this error:但后来我遇到了这个错误:

error TS2322: Type '(event: EventWithMetadata, run: Run | undefined) => { startTime: number; deviceId: string; }' is not assignable to type 'SideEffect'.
  Types of parameters 'event' and 'event' are incompatible.
    Type 'Event' is not assignable to type 'EventWithMetadata'.
      Property 'deviceId' is missing in type 'Event' but required in type '{ deviceId: string; }'.

which makes sense: Typescript thinks I'm trying to downcast Event into EventWithMetadata , which obviously can't happen.这是有道理的: Typescript 认为我正在尝试将Event向下转换为EventWithMetadata ,这显然不可能发生。

After a lot of searching on generics and overloads and union types, the best answer I could find is this one .在对 generics 以及重载和联合类型进行了大量搜索之后,我能找到的最佳答案是这个 I really hate the result and wouldn't want to make it a pattern:我真的很讨厌这个结果,不想让它成为一种模式:

const updateTiming: SideEffect = (inputEvent: Event, run) => {
  const event = inputEvent as EventWithMetadata;   // <- This sucks.
  log.debug('updateTiming: %j', event);
  return {
    lastUpdated: event.timestamp,
    deviceId: event.deviceId,
  };
};

Are there better solutions than casting?有比铸造更好的解决方案吗?

If you need updateTiming to handle EventWithMetadata specifically, then the type of updateTiming needs to know about EventWithMetadata .如果你需要updateTiming专门处理EventWithMetadata ,那么updateTiming的类型需要知道EventWithMetadata In order for SideEffect to know about EventWithMetadata and still work with other Event types, you probably want it to be generic in the particular subtype of Event that it handles:为了让SideEffect了解EventWithMetadata并仍然与其他Event类型一起工作,您可能希望它在它处理的特定Event子类型中是通用的:

type SideEffect<T extends Event> =
    (event: T, run?: Run) => Partial<Run>;

Then updateTiming is a SideEffect<EventWithMetadata> and not just a SideEffect :然后updateTiming是一个SideEffect<EventWithMetadata>而不仅仅是一个SideEffect

const updateTiming: SideEffect<EventWithMetadata> = (event, run) => {
    console.debug('updateTiming: %j', event);
    return {
        lastUpdated: event.timestamp,
        deviceId: event.deviceId,
    };
}; // okay

Note that it's not possible to hold onto a SideEffect without knowing what it handles, but that's a good thing, because otherwise you could actually pass it the wrong Event .请注意,不可能在不知道 SideEffect 处理什么的情况下保留SideEffect ,但这是一件好事,因为否则您实际上可能会传递错误的Event If you think you need a SideEffect[] without a type argument, then you will have to refactor so that you can distinguish events and event handlers somehow via a test (ideally both handlers and events would be a discriminated union but otherwise you could write custom type guard functions ).如果您认为您需要一个没有类型参数的SideEffect[] ,那么您将不得不重构,以便您可以通过测试以某种方式区分事件和事件处理程序(理想情况下,处理程序和事件都是一个可区分的联合,否则您可以编写自定义类型保护函数)。 But that's out of scope for this question as asked.但是对于所问的这个问题,这是 scope 之外的。

Playground link to code 游乐场代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM