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

TypeScript:如何同时保留传入函数返回类型并推断参数类型?

实现同时保留函数额外属性与参数类型推断

这完全是可行的,并非TypeScript的bug,而是泛型推断逻辑的设计特性导致了之前的两种写法各有局限。我们来拆解问题并给出解决方案:

为什么原有写法存在缺陷

1. testExtends的问题

testExtends定义了两个独立的泛型参数Arg和Fn,其中Fn extends UnaryFunction<Arg, any>。TypeScript会优先从第一个参数arg推断Arg的类型(这里是string),但不会反向从Fn的参数类型去修正Arg。这就导致mapWithCaller中的回调函数参数x无法被正确推断为string(反而被推断为any),进而触发x.length的类型错误;虽然它保留了Fn的完整类型(包括num属性),但参数类型推断失效了。

2. testInferred的问题

testInferred将fn直接约束为UnaryFunction<Arg, Out>,这会让TypeScript把传入的fn(实际是WithNumber类型)“降级”为基础的UnaryFunction类型。因此返回值丢失了WithNumber特有的num属性,导致访问.num时报错。

解决方案:从函数类型中提取参数类型

我们可以通过单个泛型参数捕获完整的函数类型,再用TypeScript内置的Parameters工具类型从该函数类型中提取参数类型,以此约束arg的类型。这样既保留了函数的完整类型(包括额外属性),又能正确推断回调函数的参数类型:

interface UnaryFunction<In, Out> { (arg: In): Out }
interface WithNumber<In, Out> extends UnaryFunction<In, Out> { num: number }

function mapWithCaller<From, To>(morphism: UnaryFunction<From, To>): WithNumber<From, To> {
  return Object.assign((x: From) => morphism(x), {num: 5})
}

// 正确的实现
function test<Fn extends UnaryFunction<any, any>>(arg: Parameters<Fn>[0], fn: Fn): Fn {
  return fn;
}

// 现在既可以正确推断x的类型为string,又能访问num属性
test('1', mapWithCaller(x => x.length)).num; // 完全正常,无类型错误

原理说明

  • 泛型Fn会直接捕获传入函数的完整类型(这里是WithNumber<string, number>),保留了num属性和函数签名。
  • Parameters<Fn>[0]从Fn的函数签名中提取出参数类型(string),让arg的类型与fn的参数类型严格对齐,从而触发TypeScript对mapWithCaller回调函数参数x的正确推断。

这种写法利用了TypeScript的类型推断优先级和工具类型的能力,完美解决了你的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:38:50