【问题标题】:TypeScript Generics problem: type T (extends Type) is not assignable to TTypeScript 泛型问题:类型 T(扩展类型)不可分配给 T
【发布时间】:2020-07-31 03:11:01
【问题描述】:

我对 Deno 中的 TypeScript 泛型有疑问。

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'.
}

我的目的是强制作为第二个参数传递给 hook 函数的回调返回与其参数相同类型的结果 OR null OR undefined 并且此类型应该是 Uint8Array 或已经解析Packet(或其子类),仅此而已。并且hook 函数应该存储这个钩子回调以供进一步使用。 Deno 的 TypeScript 编译器版本在向对象(使用接口 HookCallbackInfo 声明)数组添加回调时出现恐慌。

我想我在泛型的某个地方犯了一个错误,因为我无法正确理解它们如何在 TypeScript 中使用这些多种类型定义(例如 Type1 | Type2 | Type3)。你能解释一下我到底做错了什么以及如何以正确的方式做我想做的事吗?

【问题讨论】:

    标签: typescript generics deno


    【解决方案1】:

    您的callbacks 数组是HookCallbackInfo&lt;PacketData&gt; 值的数组,这意味着它们的callback 字段必须包含(data: PacketData) =&gt; HookCallbackResult&lt;PacketData&gt; 类型的函数。在您的 hook 函数中,您传递了一些未知类型的回调 T 扩展 PacketData,因此您尝试提供的回调是 (data: T) =&gt; HookCallbackResult&lt;T&gt; 类型的函数。

    这就是问题所在:callbacks 需要可以在 any PacketData 值上调用的函数,而您试图为它提供只能在 上调用的回调一些 PacketData 值。类型系统会检测并阻止您这样做。

    要了解为什么这很重要,请考虑如果类型系统没有检查会出现什么问题:

    const myFn: (v: Packet) => null = ...
    hook<Packet>("any", myFn);
    
    const array: Uint8Array = ...
    callbacks[0](array);  // we've called myFn with the wrong argument type!
    

    【讨论】:

    • 实际上,如果钩子函数的第一个参数是“any”,它将是 Uint8Array。对于每个 SpecificPacket 类型的回调将是HookCallback&lt;SpecificPacket&gt;。当这些回调将被处理时,我将检查它并将每个传入数据包的“任何”回调调用为 RAW,如果数据包签名作为 Packet 类的子类(由操作码确定)实现,则也调用其特定的回调对于它的解析版本。那么我应该如何以适当的方式实现呢?
    • 考虑到我想要的实现,我请你解释一下这些数据类型的正确通用定义,当然,这不会恐慌,而不是实现本身。那么你能完成你的答案吗?
    猜你喜欢
    • 2018-09-19
    • 2021-11-16
    • 2019-10-18
    • 2016-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-11
    • 2022-11-11
    相关资源
    最近更新 更多