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

TypeScript中带函数提示的泛型类型与属性接口实现

TypeScript Handler类类型约束与自动补全实现

需求概述

需要实现一个Handler类,满足:

  • 仅接受包含至少一个带返回值函数属性的接口作为泛型参数,无合法属性的接口会触发报错
  • send方法参数需严格校验并自动补全:
    • 第一个参数仅能选择合法的解析器属性(包含带返回值函数的属性)
    • 第二个参数仅能选择对应解析器下的带返回值函数
    • 第三个参数payload类型必须匹配所选函数的返回值类型

解决方案代码

// 返回类型定义
interface DataA {
  value: number;
}

interface DataB {
  text: string;
}

interface DataC {
  has: boolean;
}

// 合法解析器:包含至少一个带返回值的函数
interface ResolverA {
  callA(...args: any): DataA;
  callB(): DataB;
}

interface ResolverB {
  value: number; // 非函数,不会被send方法选中
  resolve(): DataC;
}

// 非法解析器:无带返回值的函数
interface ResolverC {
  text: string;
}

// 合法Resolvers接口:包含至少一个合法解析器
interface ResolversShouldWork {
  has: boolean; // 非解析器属性,不会被send方法选中
  resolverA: ResolverA;
  resolverB: ResolverB;
  resolverC: ResolverC; // 非法解析器,不会被send方法选中
}

// 非法Resolvers接口:无合法解析器
interface ResolversShouldntWorkA {
  item: object;
}

// 非法Resolvers接口:仅含非法解析器
interface ResolversShouldntWorkB {
  resolverC: ResolverC;
}

// 工具类型:提取类型T中所有值为带返回值函数的键(排除返回void的函数)
type PickMethodKeys<T> = {
  [K in keyof T]: T[K] extends (...args: any) => infer R ? (R extends void ? never : K) : never;
}[keyof T];

// 工具类型:判断T是否包含至少一个带返回值的函数,无则返回never
type HasAtLeastOneMethod<T> = PickMethodKeys<T> extends never ? never : T;

// 工具类型:从Resolvers中提取所有合法解析器的键
type ValidResolverKeys<TResolvers> = {
  [K in keyof TResolvers]: TResolvers[K] extends HasAtLeastOneMethod<TResolvers[K]> ? K : never;
}[keyof TResolvers];

class Handler<TResolvers extends Record<string, unknown>> {
  // 强制校验泛型参数必须包含至少一个合法解析器
  constructor() {
    type Check = ValidResolverKeys<TResolvers> extends never ? never : unknown;
    const _check: Check = true;
  }

  public send<
    TResolver extends ValidResolverKeys<TResolvers>,
    TMethod extends PickMethodKeys<TResolvers[TResolver]>,
    TPayload extends ReturnType<TResolvers[TResolver][TMethod]>
  >(resolver: TResolver, method: TMethod, payload: TPayload) {
    // 业务逻辑实现
  }
}

// 测试用例验证
const handlerA = new Handler<ResolversShouldWork>(); // ✅ 正常通过
const handlerB = new Handler<ResolversShouldntWorkA>(); // ❌ 报错:无合法解析器
const handlerC = new Handler<ResolversShouldntWorkB>(); // ❌ 报错:无合法解析器

handlerA.send('has', null, null); // ❌ 报错:'has'不是合法解析器
handlerA.send('resolverA', 'callA', { value: 5 }); // ✅ 正常,payload匹配DataA
handlerA.send('resolverA', 'callB', { text: 'hi' }); // ✅ 正常,payload匹配DataB
handlerA.send('resolverB', 'resolve', { has: true }); // ✅ 正常,payload匹配DataC
handlerA.send('resolverB', 'value', null); // ❌ 报错:'value'不是带返回值的函数
handlerA.send('resolverC', null, null); // ❌ 报错:'resolverC'不是合法解析器

关键类型解释

  1. PickMethodKeys<T>:遍历类型T的所有键,筛选出值为带返回值函数的键(排除返回void的函数),无符合条件的键时返回never。
  2. HasAtLeastOneMethod<T>:判断类型T是否包含至少一个带返回值的函数,若没有则返回never,用于约束解析器的合法性。
  3. ValidResolverKeys<TResolvers>:从泛型参数TResolvers中筛选出值为合法解析器的键,无合法解析器时返回never。
  4. 构造函数类型校验:通过Check类型强制要求TResolvers必须包含至少一个合法解析器,否则创建实例时会触发类型错误。
  5. send方法泛型约束:
    • TResolver限制第一个参数只能选合法解析器的键
    • TMethod限制第二个参数只能选对应解析器下的带返回值函数
    • TPayload直接绑定所选函数的返回值类型,确保参数类型匹配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 11:42:38