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

如何在TypeScript中定义符合Json约束的泛型函数数组类型

你遇到的问题本质是TypeScript没有直接提供「存在泛型」的语法支持,你需要的类型等价于「存在任意符合Json约束的类型A,使得当前值是Foo类型」,而TS原生泛型默认是「对所有符合约束的A都成立」,这就是为什么直接写Foo<Json>[]会报错的原因:函数参数类型是逆变的,Foo<number>、Foo<string>这类具体类型无法赋值给更宽泛的Foo<Json>类型。

以下是两种可行的实现方案:

方案1:使用辅助构造函数(推荐)

通过泛型辅助函数自动推导数组内每个元素的类型,同时做合法性校验,不需要手动定义联合类型:

type Json = string | number | boolean | null | Json[] | { [property: string]: Json };
type Foo<A extends Json> = (n: A) => boolean;

// 辅助构造函数,入参是你要传入的Foo数组,返回值自动加约束
function createFooArray<T extends Foo<any>[]>(arr: [...T]) {
  // 约束每个元素的泛型参数必须符合Json要求
  type ValidFoo<T> = T extends Foo<infer A> ? A extends Json ? T : never : never;
  return arr as ValidFoo<T[number]>[];
}

// 示例:合法数组,无报错
const a: Foo<number> = (n) => n + 1 > 0;
const b: Foo<string> = (n) => n.endsWith("a");
const foos = createFooArray([a, b]);

// 示例:非法数组会触发类型报错
const invalidFoos = createFooArray([a, b, (n: HTMLElement) => true]); 
// 报错原因:HTMLElement不满足Json类型约束

这个方案零运行时开销,编译后辅助函数会被优化为普通数组,同时完整保留类型校验能力。

方案2:直接定义通用Foo类型

如果你不想使用辅助函数,也可以直接定义覆盖所有合法场景的SomeFoo类型:

type Json = string | number | boolean | null | Json[] | { [property: string]: Json };
type Foo<A extends Json> = (n: A) => boolean;

type SomeFoo = Foo<Json> extends Foo<infer A> ? A extends Json ? Foo<A> : never : never;

// 合法
const foos: SomeFoo[] = [a, b];
// 非法会报错
const invalidFoos: SomeFoo[] = [a, b, (n: HTMLElement) => true];

成对函数约束场景实现

针对你提到的「第一个函数返回值与第二个函数入参类型匹配」的业务场景,同样可以用辅助构造函数实现:

type Json = string | number | boolean | null | Json[] | { [property: string]: Json };

// 定义成对函数的类型
type SerializePair<A extends Json, B extends Json> = {
  serialize: (input: A) => B,
  deserialize: (input: B) => A
}

// 辅助构造函数
function createPairArray<T extends SerializePair<any, any>[]>(arr: [...T]) {
  type ValidPair<T> = T extends SerializePair<infer A, infer B> 
    ? A extends Json ? B extends Json ? T : never : never 
    : never;
  return arr as ValidPair<T[number]>[];
}

// 合法用法
const pairs = createPairArray([
  { serialize: (n: number) => n.toString(), deserialize: (s: string) => parseInt(s) },
  { serialize: (arr: number[]) => arr.join(","), deserialize: (str: string) => str.split(",").map(Number) }
]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 20:27:02