You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript无法从类模板参数推导所需返回类型的原因及解决方案

问题分析与解决方案

我来帮你拆解这个TypeScript类型推断的问题,并给出适配你去中心化Hook框架需求的解决方案。

问题根源

第一个示例的问题

你定义的on函数把ArgType、ReturnType和T作为三个独立泛型参数声明,但TypeScript无法在instanceof分支里将泛型参数ReturnType与PostCreate的具体返回类型number关联起来。虽然t被细化为PostCreate实例,但泛型参数ReturnType仍然保持着初始的约束(未被细化),所以TypeScript认为number无法赋值给未知的ReturnType。

第二个示例的问题

使用infer的条件类型InferReturnType<T>是基于泛型T的,而在函数内部的分支里,即使t被细化为PostCreate,泛型T依然是整个函数的通用参数,TypeScript无法在分支内解析InferReturnType<T>的具体值,导致它始终被视为unknown。

去中心化解决方案

方案1:基于泛型推断的灵活实现(推荐)

调整泛型声明方式,让返回类型直接从传入的Hook实例类型中推断,配合类型断言解决分支内的类型匹配问题:

abstract class Hook<ArgsType, ReturnType = void> { 
  constructor(public readonly args: ArgsType){}
}

// 独立定义Hook类,完全去中心化
class PostCreate extends Hook<{key: string}, number>{}
class PreUpdate extends Hook<{id: number}, boolean>{}
class PostDelete extends Hook<{uuid: string}, void>{}

function on<T extends Hook<any, any>>(t: T): T extends Hook<any, infer R> ? R | undefined : undefined {
  if (t instanceof PostCreate) {
    console.log(t.args.key); // 正确识别args类型
    // 基于当前t的具体类型断言返回值
    return 5 as typeof t extends Hook<any, infer R> ? R : never;
  } else if (t instanceof PreUpdate) {
    console.log(t.args.id);
    return true as typeof t extends Hook<any, infer R> ? R : never;
  } else if (t instanceof PostDelete) {
    console.log(t.args.uuid);
    return undefined as typeof t extends Hook<any, infer R> ? R : never;
  }
  return undefined;
}

// 调用时自动推断返回类型
const postResult = on(new PostCreate({key: "my value"})); // number | undefined
const updateResult = on(new PreUpdate({id: 123})); // boolean | undefined
const deleteResult = on(new PostDelete({uuid: "abc"})); // void | undefined

优点:完全去中心化,新增Hook类只需添加对应的instanceof分支即可,无需修改全局类型定义。

方案2:函数重载实现精准类型推断

如果你的Hook类型数量相对固定,可以用函数重载实现最精准的类型推断:

abstract class Hook<ArgsType, ReturnType = void> { 
  constructor(public readonly args: ArgsType){}
}

class PostCreate extends Hook<{key: string}, number>{}
class PreUpdate extends Hook<{id: number}, boolean>{}

// 为每个Hook类型定义重载签名
function on(t: PostCreate): number | undefined;
function on(t: PreUpdate): boolean | undefined;
// 通用实现
function on(t: Hook<any, any>): any | undefined {
  if (t instanceof PostCreate) {
    console.log(t.args.key);
    return 5;
  } else if (t instanceof PreUpdate) {
    console.log(t.args.id);
    return true;
  }
  return undefined;
}

// 类型推断完全精准
const postResult = on(new PostCreate({key: "test"})); // number | undefined
const updateResult = on(new PreUpdate({id: 123})); // boolean | undefined

优点:类型推断最精准,无需类型断言;缺点:新增Hook类需要手动添加对应的重载签名,扩展性稍弱。

方案3:类型谓词辅助的类型细化

通过自定义类型谓词,让TypeScript更精准地识别Hook类型及其返回值:

abstract class Hook<ArgsType, ReturnType = void> { 
  constructor(public readonly args: ArgsType){}
}

// 自定义类型谓词,用于细化Hook的返回类型
function isHookType<T extends Hook<any, any>>(
  t: Hook<any, any>, 
  ctor: new (...args: any[]) => T
): t is T {
  return t instanceof ctor;
}

class PostCreate extends Hook<{key: string}, number>{}
class PreDelete extends Hook<{id: number}, string>{}

function on<T extends Hook<any, any>>(t: T): T extends Hook<any, infer R> ? R | undefined : undefined {
  if (isHookType(t, PostCreate)) {
    console.log(t.args.key);
    return 5 as T extends Hook<any, infer R> ? R : never;
  } else if (isHookType(t, PreDelete)) {
    console.log(t.args.id);
    return "deleted" as T extends Hook<any, infer R> ? R : never;
  }
  return undefined;
}

const postResult = on(new PostCreate({key: "test"})); // number | undefined
const deleteResult = on(new PreDelete({id: 456})); // string | undefined

优点:类型谓词可复用,适合复杂场景下的类型细化;缺点:需要额外定义类型谓词函数。

内容的提问来源于stack exchange,提问作者Pinna_be

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 13:42:34