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

如何在TypeScript中泛型约束高阶函数接收的函数参数?

约束函数参数为object子类型并自动推断返回类型的实现方案

需求与问题

需要实现一个函数createObjectAcceptingFunctions,接收一组函数作为参数,需满足:

  • 每个传入函数的参数必须是object的子类型;
  • 传入函数的具体类型(包括参数和返回值)能被完整推断,比如传入[(x: {a:1}) => void]时,返回的类型要与该输入类型完全一致。

尝试实现时遇到逆变问题:若直接指定函数接收(input: object) => void的子类型,object会被视为逆变约束,导致仅允许object的超类型(如unknown),与需求中允许子类型的要求相反。

以下是最初的尝试代码:

type InputFunction<$I> = <$Input extends $I>(input: $Input) => void

declare const createObjectAccepingFunctions: <const $Fs extends InputFunction<object>[]>(fs: $Fs) => { items: $Fs }

const result = createObjectAccepingFunctions([
    (_: { a: 1 }) => {}, // 应当通过校验
    (_: string) => {}, // 应当校验失败
])

result.items

解决方案

核心思路是利用协变类型约束,明确每个传入函数的参数类型必须是object的子类型,同时保留函数的具体类型信息以实现自动推断。

方案一:明确泛型函数参数约束

// 定义参数为object子类型的函数类型
type ObjectParamFunction<T extends object> = (input: T) => unknown

declare const createObjectAcceptingFunctions: <const Fs extends ObjectParamFunction<any>[]>(
    fs: Fs extends ObjectParamFunction<infer T>[] ? T extends object ? Fs : never : never
) => { items: Fs }

// 合法测试:参数均为object子类型,类型完整推断
const validResult = createObjectAcceptingFunctions([
    (_: { a: 1 }) => {},
    (_: { b: string }) => 42,
])
// validResult.items 类型为:readonly [(_: { a: 1; }) => void, (_: { b: string; }) => number]

// 非法测试:参数为非object类型,触发类型错误
const invalidResult = createObjectAcceptingFunctions([
    (_: string) => {}, // 错误:类型'string'不满足约束'object'
])

方案二:简洁的推断与约束写法(TS 4.7+)

declare const createObjectAcceptingFunctions: <const Fs extends readonly ((input: infer T) => unknown)[]>(
    fs: T extends object ? Fs : never
) => { items: Fs }

// 测试效果与方案一完全一致
const validResult = createObjectAcceptingFunctions([
    (_: { id: number }) => "ok",
])
// validResult.items 类型为:readonly [(_: { id: number; }) => string]

const invalidResult = createObjectAcceptingFunctions([
    (_: number) => {}, // 错误:类型'number'不满足约束'object'
])

原理说明

  • 通过const泛型参数保留数组中每个函数的具体字面量类型,确保返回类型与输入完全一致;
  • 利用条件类型T extends object ? Fs : never校验每个函数的参数类型T是否为object的子类型,非object类型会触发never,从而抛出类型错误;
  • 避开了逆变约束的问题,直接对函数的参数类型进行协变校验,符合需求中允许object子类型的要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 15:47:04