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

无需泛型,基于参数判别TypeScript函数返回类型

解决方案:判别式元组实现泛型多态函数的返回类型匹配问题

问题核心在于TypeScript无法自动将控制流分支中的返回值类型与PolymorphicFn泛型定义的返回类型PrimitiveOf<TUPLE[number]>建立关联。以下是两种可行的解决思路:


方案一:优化类型守卫+类型断言

先将参数类型守卫调整为精确匹配目标元组类型,再通过类型断言让TypeScript确认返回值符合泛型约束:

type PrimitiveOf<A extends string | number> = A extends string
  ? string
  : A extends number
    ? number
    : never;

type PolymorphicFn<TUPLES extends (string | number)[]> = <TUPLE extends TUPLES>(
  ...args: TUPLE
) => PrimitiveOf<TUPLE[number]>;

// 精确匹配[string, string]元组的类型守卫
const isStringTuple = (tuple: unknown[]): tuple is [string, string] =>
  tuple.length === 2 && typeof tuple[0] === 'string' && typeof tuple[1] === 'string';

// 精确匹配[number, number]元组的类型守卫
const isNumberTuple = (tuple: unknown[]): tuple is [number, number] =>
  tuple.length === 2 && typeof tuple[0] === 'number' && typeof tuple[1] === 'number';

const add: PolymorphicFn<[string, string] | [number, number]> = (...tuple) => {
  if (isStringTuple(tuple)) {
    const [a, b] = tuple;
    return `${a}${b}` as PrimitiveOf<typeof tuple[number]>;
  }

  if (isNumberTuple(tuple)) {
    const [a, b] = tuple;
    return a + b as PrimitiveOf<typeof tuple[number]>;
  }

  // 兜底处理(理论上不会触发,因参数已被约束为指定元组)
  throw new Error('Invalid arguments');
};

const num = add(1, 2); // 类型推断为number
const str = add('1', '2'); // 类型推断为string

方案二:重构泛型函数类型定义

换一种方式定义PolymorphicFn,让返回类型与参数元组的关联更明确,消除TypeScript的推断盲区:

type PrimitiveOf<A extends string | number> = A extends string
  ? string
  : A extends number
    ? number
    : never;

// 针对每个元组类型单独生成函数签名,再合并为联合类型
type PolymorphicFn<T extends readonly (readonly (string | number)[])[]> = 
  T[number] extends infer U 
    ? U extends readonly [infer A, infer B]
      ? A extends string 
        ? B extends string 
          ? (...args: U) => string 
          : never
        : A extends number 
          ? B extends number 
            ? (...args: U) => number 
            : never
          : never
      : never
    : never;

// 定义合法的参数元组集合
type ValidTuples = [[string, string], [number, number]];

const add: PolymorphicFn<ValidTuples> = (...tuple) => {
  const [a, b] = tuple;
  if (typeof a === 'string' && typeof b === 'string') {
    return `${a}${b}`;
  } else if (typeof a === 'number' && typeof b === 'number') {
    return a + b;
  }
  throw new Error('Invalid arguments');
};

const num = add(1, 2); // number
const str = add('1', '2'); // string

关键说明

  • 方案一保留了你原有的泛型函数结构,通过精确的元组类型守卫缩小参数范围,再用类型断言填补TypeScript的推断缺口。
  • 方案二利用映射类型将每个合法元组对应到具体的函数签名,让TypeScript能直接识别每个分支的返回类型,无需额外断言。

两种方案都能实现你需要的多态效果,且符合泛型计算的需求,避免了函数重载的写法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:41:13