簡體   English   中英

Typescript 通用 通用

[英]Typescript Generic Generic

不確定我追求的是什么 typescript 功能,但我認為我需要一個通用的泛型(如果那是 typescript 功能)。 下面是我的要求。

我想表示事件/事件處理程序元組的集合。 以下是我到目前為止所擁有的。

interface Event {
 type: string;
}

type EventType<T extends Event> = T['type'];
type EventHandler<T extends Event> = (event:T): void;

// so far so good
type EventTypeAndHandlerTuple <T extends Event> = [EventType<T>, EventHandler<T>];


// this is where I wish I could do better. I am forced to used any
type EventTypeAndHandlerTuples = EventTypeAndHandlerTuple<any>[]

我希望能夠代表這樣的代碼

const todoAddedEvent: TodoAddedEvent = {...}
const handleTodoAddedEvent: TodoAddedHandler = (event: TodoAddedEvent) => {...};
const todoRemovedEvent: TodoRemovedEvent = {...}
const handleTodoRemovedEvent: TodoRemovedHandler = (event: TodoRemovedEvent) => {...}

// HELP ME GIVE THIS THING A TYPE 😢
const eventTypeToHandlerTuples = [
 [todoAddedEvent, handleTodoAddedEvent],
 [todoRemovedEvent, handleTodoRemovedEvent]
];

我想要一個eventTypeToHandlerTuples類型,如果它是關聯事件類型和事件處理程序元組的集合,它就會通過。 如果事件類型與不正確的處理程序相關聯,我希望此類型表示失敗。 例如,我希望這失敗

// this should fail because the removed event is being paired with the add event handler.
const eventTypeToHandlerTuples = [
 [todoRemovedEvent, handleTodoAddedEvent], 
];

干杯,並提前感謝您的意見

做到這一點的簡單方法是收集您關心的所有Event類型的聯合; 這可能是可能的,因為您顯然希望它們的type屬性是唯一的字符串文字類型,這可用於區分這樣的聯合 如果是這樣,您定義聯合:

type Events = TodoAddedEvent | TodoRemovedEvent;

然后使用它來描述Events聯合中每個元素T的所有可能的EventTypeAndHandlerTuple<T>類型的聯合,使用一些聯合構建技術,如分布式條件類型

type UnionOfEventTypeAndHandlerTuples = Events extends infer T ?
  T extends Event ? EventTypeAndHandlerTuple<T> : never : never;
// type UnionOfEventTypeAndHandlerTuples = 
// EventTypeAndHandlerTuple<TodoAddedEvent> | EventTypeAndHandlerTuple<TodoRemovedEvent>

一旦你有了這個,你想要的類型就是UnionOfEventTypeAndHandlerTuples[]

// okay
const eventTypeToHandlerTuples: UnionOfEventTypeAndHandlerTuples[] = [
  [todoAddedEvent, handleTodoAddedEvent],
  [todoRemovedEvent, handleTodoRemovedEvent]
];

// error
const badEventTypeToHandlerTuples: UnionOfEventTypeAndHandlerTuples[] = [
  [todoRemovedEvent, handleTodoAddedEvent], // error!
];

如果您沒有預定義的聯合,那么事情就會變得更加困難。 在 TypeScript (參見microsoft/TypeScript#14466 )中,沒有對存在泛型類型的原生支持,沒有像EventTypeAndHandlerTuple<exists T extends Event>[]這樣的特定類型,其中exists T表示“我不知道也不關心T是什么,我只關心它的存在”。 您可以將存在類型視為所有可能匹配類型的“無限聯合”。

相反,您需要做其他事情。 Probably the most reasonable for TypeScript is to use the regular old generic types in TypeScript (these are called universal instead of existential and act like "infinite intersections" instead of "infinite unions"), and use a helper function to make the compiler infer the對你來說是通用的,所以你不必寫出來:

const asEventTypeToHandlerTuples = <T extends Event[]>(
  tuples: [...{ [I in keyof T]: EventTypeAndHandlerTuple<Extract<T[I], Event>> }]
) => tuples;

助手 function asEventTypeToHandlerTuples()接受一個名為tuples的參數並返回它。 此參數的類型被限制為映射元組類型,其中它將Event類型數組轉換為相應的EventTypeAndHandlerTuple類型數組。 如果推斷T的類型為[TodoAddedEvent, TodoRemovedEvent, SomeOtherEvent] ,則tuples()的類型被限制為[EventTypeAndHandlerTuple<TodoAddedEvent>, EventTypeAndHandlerTuple<TodoRemovedEvent>, EventTypeAndHandlerTuple<SomeOtherEvent>] 讓我們看看它是如何工作的。 我們不再注釋變量的類型,而是讓編譯器推斷它們:

// okay
const eventTypeToHandlerTuples = asEventTypeToHandlerTuples([
  [todoAddedEvent, handleTodoAddedEvent],
  [todoRemovedEvent, handleTodoRemovedEvent]
]);

// error
const badEventTypeToHandlerTuples = asEventTypeToHandlerTuples([
  [todoRemovedEvent, handleTodoAddedEvent], // error!
]);

eventTypeToHandlerTuples分配工作沒有問題,但badEventTypeToHandlerTuples給出錯誤,因為它推斷其類型為[EventTypeAndHandlerTuple<TodoAddedEvent>] ,但todoRemovedEvent不可分配給EventType<TodoAddedEvent> ,你會得到所需的錯誤。


就我個人而言,我會嘗試使用類似Events的聯合,因為處理特定類型的聯合要比在代碼庫中拖動泛型類型參數來表示約束要容易得多。 但如果這不可行,通用輔助函數通常是您正在尋找的那種不可表示的存在類型的可接受替代品。

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM