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

如何在TypeScript中正确为包含同输入输出类型的泛型函数的Record进行类型定义?

如何在TypeScript中正确为包含同输入输出类型的泛型函数的Record进行类型定义?

我太懂你这个痛点了——用InOutFunc<any>确实会把整个Record的类型安全彻底搞崩,而且你说的协变逆变问题完全说到点子上了:InOutFunc<T>在T上是不变类型,输入参数要逆变、返回值要协变,根本找不到一个统一的T能同时罩住所有函数的输入输出约束。

先给你上最直接的解决方案,先把你当前的代码救回来:

方案1:用映射类型精准定义具体Record类型

如果你已经明确知道Record里每个键对应的T类型(比如foo是number,bar是string),用映射类型可以给每个函数单独指定类型,完全不会丢失信息:

export type InOutFunc<T> = (input: T) => T;

// 定义一个泛型Record类型,每个键对应自己的InOutFunc
type InOutRecord<T extends Record<string, unknown>> = {
  [K in keyof T]: InOutFunc<T[K]>;
};

// 针对你的场景,写具体的Record类型
type MyInOutRecord = InOutRecord<{
  foo: number;
  bar: string;
}>;

// 现在赋值的时候,类型检查严丝合缝
const inOutRecord: MyInOutRecord = {
  foo: (a: number): number => 1,
  bar: (a: string): string => 'abc',
};

// 访问foo的时候,类型还是(number) => number,完全保留了原来的类型
inOutRecord.foo(123); // 必须传number,传别的直接报错
inOutRecord.bar('test'); // 同理,必须传string

方案2:让TypeScript自动推断类型(不用手动写每个键的T)

如果不想提前写死每个键的T类型,用泛型函数来创建Record就行,TypeScript会自动帮你推断每个函数的T:

export type InOutFunc<T> = (input: T) => T;

// 写个泛型函数,专门用来创建这种Record
function createInOutRecord<T extends Record<string, unknown>>(
  record: { [K in keyof T]: InOutFunc<T[K]> }
) {
  return record;
}

// 直接传对象,TypeScript会自动识别foo是number、bar是string
const inOutRecord = createInOutRecord({
  foo: (a: number): number => 1,
  bar: (a: string): string => 'abc',
});

// 用的时候类型完全安全,比如foo的返回值还是number
const fooResult = inOutRecord.foo(456);
const barResult = inOutRecord.bar('hello');

为啥原来的写法不行?

你之前用Record<string, InOutFunc<any>>的问题,本质是any直接把所有具体类型都抹没了——不管传什么参数、返回什么值,TypeScript都不会管。而且你说的协变逆变冲突确实是核心:InOutFunc<T>是不变类型,输入要逆变、返回要协变,所以:

  • 用InOutFunc<never>的话,输入根本传不了值,但返回值也被锁死成never,完全没用;
  • 用InOutFunc<unknown>的话,返回值是unknown(得手动断言才能用),输入又能传任意值,完全不安全;

这俩根本没法兼容,所以必须放弃“用一个统一T覆盖所有函数”的思路,转而给每个键单独指定或推断T类型。

处理通用场景的小技巧

如果要写一个能处理任意这类Record的通用函数,别用any或unknown,用泛型就能保留完整的类型安全:

function processInOutRecord<T extends Record<string, unknown>>(
  record: { [K in keyof T]: InOutFunc<T[K]> }
) {
  // 这里处理每个函数的时候,TypeScript都知道每个键对应的T类型
  Object.entries(record).forEach(([key, func]) => {
    // 比如如果是foo,func的类型就是(number) => number,完全安全
  });
}

这样不管你传入什么样的InOutRecord,函数内部都能保持类型安全,不会出现类型丢失的问题。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 10:38:04