I have a problem with TypeScript generics in Deno.
abstract class Packet {
public abstract read(): Uint8Array;
public abstract write(data: Uint8Array): void;
}
type PacketData = Packet | Uint8Array;
type HookCallbackResult<T extends PacketData> = T | null | undefined;
type HookCallback<T extends PacketData> = (data: T) => HookCallbackResult<T>;
interface HookCallbackInfo<T extends PacketData> {
packetType: typeof Packet | "any";
callback: HookCallback<T>;
}
let callbacks: HookCallbackInfo<PacketData>[] = [];
function hook<T extends PacketData>(packetType: typeof Packet | "any", callback: HookCallback<T>) {
callbacks.push({ packetType, callback });
// ^^^^^^^^
// error: TS2322 [ERROR]: Type 'HookCallback<T>' is not assignable to type 'HookCallback<PacketData>'.
// Types of parameters 'data' and 'data' are incompatible.
// Type 'PacketData' is not assignable to type 'T'.
// 'PacketData' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'PacketData'.
// Type 'Packet' is not assignable to type 'T'.
// 'Packet' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'PacketData'.
}
My purpose is to force a callback which is passed as 2nd argument to hook
function to return a result of the same type as its argument OR null
OR undefined
and this type should be Uint8Array
or already parsed Packet
(or its child class) and nothing else. And hook
function should store this hook callback for further use. Deno's version of TypeScript compiler panics on adding a callback to an object (declared with interface HookCallbackInfo
) array.
I guess I made a mistake somewhere with generics cause I don't understand properly how they work in TypeScript with these multiple type definitions (like Type1 | Type2 | Type3
). Could you pls explain me what exactly I did wrong and how to do what I want in the proper way?