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

如何基于type与multiple为Record值推导并限制TypeScript类型?

基于type和multiple限制函数参数Record类型的解决方案

需求描述

需要为useQuery函数的参数创建类型,使其能根据type字符串和可选的multiple布尔值,自动限制ref和whitelist的类型。示例用法如下:

useQuery({
  a: {
    ref: ref(''), // ref类型为Ref<string>
    type: 'string',
  },
  b: {
    whitelist: [1, 2, 3], // whitelist类型为number[](可选)
    ref: ref([1, 2, 3]), // ref类型为Ref<number[]>
    type: 'number',
    multiple: true,
  },
});

尝试过的方案及问题

  1. 联合类型方案:无法正确处理可选的multiple参数,当不传入multiple时,ref的类型会被推断为string | string[]这类联合类型,不符合预期:
type DumbType = {
      whitelist?: string[];
      ref: Ref<string>;
      type: 'string';
    }
  | {
      whitelist?: string[];
      ref: Ref<string[]>;
      type: 'string';
      multiple: true;
    }
  | {
      whitelist?: number[];
      ref: Ref<number>;
      type: 'number';
    }
  | {
      whitelist?: number[];
      ref: Ref<number[]>;
      type: 'number';
      multiple: true;
    };

// 问题:不传入multiple时,ref被推断为string | string[]
useQuery({
 type: 'string',
 ref: ref(), 
})
  1. 映射类型方案:QueryOptions单独使用正常,但UseQueryOptions无法正确推断Record值的泛型参数,出现类型错误:
type Primitives = 'string' | 'number' | 'boolean';

export type UseQueryOptions<T> = {
  [K in keyof T]: QueryOptions<T[K]['type'], T[K]['multiple']>; // 此处报错
};

export type QueryOptions<T extends Primitives, M extends boolean | undefined> = {
  whitelist?: StringTypeToType<T, true>;
  ref: Ref<StringTypeToType<T, M>>;
  type: T;
  multiple?: M;
};

export type StringTypeToType<P extends Primitives, M extends boolean | undefined> = P extends 'string'
  ? ArrayWhenMultiple<M, string>
  : P extends 'number'
  ? ArrayWhenMultiple<M, number>
  : P extends 'boolean'
  ? ArrayWhenMultiple<M, boolean>
  : never;

export type ArrayWhenMultiple<M extends boolean | undefined, T = any> = M extends true ? T[] : T;

export type TypeToRefType<P extends Primitives, M extends boolean> = Ref<StringTypeToType<P, M>>;

export const useQuery = <P extends Primitives, m extends boolean | undefined>(options: QueryOptions<P, M>) => {};

解决方案

调整泛型约束和类型映射逻辑,让TypeScript能正确推断每个属性的类型:

// 假设Ref是Vue的Ref类型,这里定义简化版
type Ref<T> = { value: T };
const ref = <T>(val?: T): Ref<T | undefined> => ({ value: val });

type Primitives = 'string' | 'number' | 'boolean';

// 基础类型映射:将字符串类型转为TS原生类型
type PrimitiveToType<T extends Primitives> = 
  T extends 'string' ? string :
  T extends 'number' ? number :
  T extends 'boolean' ? boolean : never;

// 根据multiple决定是否转为数组类型
type TypeWithMultiple<T extends Primitives, M extends boolean | undefined> = 
  M extends true ? PrimitiveToType<T>[] : PrimitiveToType<T>;

// 单个字段的配置类型
type QueryFieldOptions<T extends Primitives, M extends boolean | undefined> = {
  type: T;
  multiple?: M;
  ref: Ref<TypeWithMultiple<T, M>>;
  whitelist?: PrimitiveToType<T>[]; // whitelist始终是对应类型的数组
};

// 允许传入多个字段的Record类型
type UseQueryOptions = Record<string, QueryFieldOptions<Primitives, boolean | undefined>>;

// 调整useQuery函数,让TS能自动推断每个字段的泛型参数
export const useQuery = <T extends UseQueryOptions>(options: T) => {};

// 测试示例
useQuery({
  a: {
    ref: ref(''), // 正确推断为Ref<string>
    type: 'string',
  },
  b: {
    whitelist: [1, 2, 3], // 正确推断为number[]
    ref: ref([1, 2, 3]), // 正确推断为Ref<number[]>
    type: 'number',
    multiple: true,
  },
  c: {
    ref: ref(true), // 正确推断为Ref<boolean>
    type: 'boolean',
  },
  d: {
    ref: ref([true, false]), // 正确推断为Ref<boolean[]>
    type: 'boolean',
    multiple: true,
  },
});

关键调整点

  • 拆分类型映射:将PrimitiveToType和TypeWithMultiple拆分,简化嵌套条件判断,让类型逻辑更清晰。
  • Record类型适配:UseQueryOptions直接定义为Record<string, QueryFieldOptions<...>>,配合函数泛型T extends UseQueryOptions,让TS自动推断每个字段的具体类型。
  • 函数泛型优化:不再手动指定P和M泛型,而是通过传入的options自动推断每个字段的type和multiple对应的类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:15:33