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

如何在TypeScript中基于记录常量的复杂类型约束函数参数

解决方案

要实现这个需求,核心是保留AllCollections的精确类型信息(不要手动指定宽泛的Record<string, AnyCollection>),然后通过条件类型动态约束函数参数和返回值。

步骤1:修正集合常量的类型推导

首先去掉AllCollections的手动类型注解,并用as const锁定字面量类型,让TypeScript能精确推导每个集合的具体类型:

import { z } from "zod";

export type HomogeneousCollection = {
  schema: z.ZodType<any>;
  type: "homogeneous";
};

export type HeterogeneousCollection = {
  schemas: Record<string, z.ZodType<any>>;
  type: "heterogeneous";
};

export type AnyCollection = HomogeneousCollection | HeterogeneousCollection;

// 自动推导精确类型,用as const锁定字面量
export const AllCollections = {
  collectionA: {
    type: "homogeneous" as const,
    schema: z.object({ foo: z.number() })
  },
  collectionB: {
    type: "heterogeneous" as const,
    schemas: {
      docA: z.object({ bar: z.number() }),
      docB: z.object({ baz: z.number() })
    }
  }
} as const;

步骤2:定义泛型辅助类型

提取集合名称类型、集合类型,并通过条件类型定义函数的参数规则:

// 提取所有集合名称的字面量类型
type CollectionNames = keyof typeof AllCollections;

// 根据集合名称获取对应的集合类型
type GetCollection<C extends CollectionNames> = typeof AllCollections[C];

// 定义函数的参数类型:根据集合类型动态生成
type FunctionArgs<C extends CollectionNames> = 
  GetCollection<C> extends HomogeneousCollection 
    ? [collectionName: C]
    : [collectionName: C, options: { documentName: keyof GetCollection<C>['schemas'] }];

// 定义返回值类型:根据参数提取对应的Zod schema的infer类型
type ReturnSchema<C extends CollectionNames, Args extends any[]> = 
  Args extends [C]
    ? z.infer<GetCollection<C>['schema']>
    : Args extends [C, { documentName: infer D }]
      ? D extends keyof GetCollection<C>['schemas']
        ? z.infer<GetCollection<C>['schemas'][D]>
        : never
      : never;

步骤3:实现约束函数

用泛型和Rest参数结合条件类型,实现符合要求的函数:

function f<C extends CollectionNames, Args extends FunctionArgs<C>>(
  ...args: Args
): ReturnSchema<C, Args> {
  const [collectionName, options] = args;
  const collection = AllCollections[collectionName];

  if (collection.type === "homogeneous") {
    return collection.schema.parse({}) as ReturnSchema<C, Args>;
  } else {
    if (!options) throw new Error("需传入documentName");
    const schema = collection.schemas[options.documentName];
    return schema.parse({}) as ReturnSchema<C, Args>;
  }
}

验证效果

现在函数会严格约束参数,并正确推导返回值:

// ✅ 编译通过,返回类型为 { foo: number }
f("collectionA");

// ✅ 编译通过,返回类型为 { baz: number }
f("collectionB", { documentName: "docB" });

// ❌ 编译失败:缺少第二个参数
f("collectionB");

// ❌ 编译失败:documentName不是有效取值
f("collectionB", { documentName: "invalidDoc" });

关键说明

  • 用as const锁定AllCollections的字面量类型是核心,这样TypeScript才能区分每个集合是同质还是异质,以及具体的文档名称。
  • 条件类型FunctionArgs动态生成参数列表,确保同质集合只传名称,异质集合必须传名称+文档名。
  • 返回类型ReturnSchema根据参数自动推导对应的Zod类型,无需手动指定泛型参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 03:25:02