简体   繁体   中英

TypeScript Generics problem: type T (extends Type) is not assignable to T

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?

Your callbacks array is an array of HookCallbackInfo<PacketData> values, which means their callback fields must contain functions of type (data: PacketData) => HookCallbackResult<PacketData> . In your hook function, you're passing in callbacks of some unknown type T that extends PacketData , so the callback you're trying to provide is a function of type (data: T) => HookCallbackResult<T> .

This is where the problem lies: callbacks needs functions that can be called on any PacketData value, whereas you're trying to provide it with callbacks that can only be called on some PacketData values. The type system detects and prevents you from doing so.

To see why this matters, consider what could go wrong if the type system didn't check this:

const myFn: (v: Packet) => null = ...
hook<Packet>("any", myFn);

const array: Uint8Array = ...
callbacks[0](array);  // we've called myFn with the wrong argument type!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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