TypeScript分布式条件类型未按预期工作问题求助
解决TypeScript查询类型中字符串字段value类型不匹配的问题
原类型定义
type MatchTypeDefault = 'NULL' | 'NOT_NULL'; type MatchTypeBoolean = 'IS'; type MatchTypeText = | 'EQUALS' | 'EQUALS_CASE_INSENSITIVE' | 'NOT_EQUALS' | 'NOT_EQUALS_CASE_INSENSITIVE' | 'BEFORE' | 'AFTER'; type MatchTypeNumber = | 'EQUALS' | 'NOT_EQUALS' | 'GREATER_THAN' | 'GREATER_THAN_OR_EQUAL' | 'LESS_THAN' | 'LESS_THAN_OR_EQUAL'; type MatchTypeArray = MatchTypeDefault | 'INCLUDES' | 'EXCLUDES'; type MatchTypeInArray = MatchTypeDefault | 'IN' | 'IN_CASE_INSENSITIVE'; type OrderParamsOrderBy<T extends {}> = { fieldName: keyof T; type: 'ASC' | 'DESC'; }; type QueryParamsTopLevel<TObject extends {}> = { conditionType: 'AND' | 'OR'; conditions: GenericQuery<TObject>[]; fieldName?: never; matchType?: never; value?: never; orderBy: OrderParamsOrderBy<TObject>[]; }; type QueryParamsCondition< TObject extends {}, TKey extends keyof TObject = keyof TObject, TMatchType extends | MatchTypeText | MatchTypeNumber | MatchTypeBoolean | MatchTypeInArray | MatchTypeArray | MatchTypeDefault = NonNullable<TObject[TKey]> extends boolean ? MatchTypeBoolean : NonNullable<TObject[TKey]> extends string ? MatchTypeText | MatchTypeInArray : NonNullable<TObject[TKey]> extends number ? MatchTypeNumber : NonNullable<TObject[TKey]> extends Array<unknown> ? MatchTypeArray : MatchTypeDefault > = { conditionType?: never; conditions?: never; fieldName: TKey; matchType: TMatchType; value: TMatchType extends MatchTypeInArray ? string[] : NonNullable<TObject[TKey]>; orderBy: OrderParamsOrderBy<TObject>[]; }; export type GenericQuery<TObject extends {}> = { [TKey in keyof TObject]: | QueryParamsTopLevel<TObject> | QueryParamsCondition<TObject, TKey>; }[keyof TObject];
问题现象
使用测试接口TestType创建查询常量时,数字、布尔字段的matchType与value类型匹配正常,但字符串字段(如id)的value类型始终为string | string[],不符合预期:
interface TestType { id: string; price: number; inStock: boolean; } // 不符合预期:matchType为'EQUALS'时,value应该只能是string,而非string | string[] const testVariable: GenericQuery<TestType> = { fieldName: 'id', matchType: 'EQUALS', value: ..., };
尝试的无效方案
曾尝试用非分布式条件类型修改QueryParamsCondition的value判断,但结果value始终为string,问题未解决:
type QueryParamsCondition< TObject extends {}, TKey extends keyof TObject = keyof TObject, TMatchType extends | MatchTypeText | MatchTypeNumber | MatchTypeBoolean | MatchTypeInArray | MatchTypeArray | MatchTypeDefault = NonNullable<TObject[TKey]> extends boolean ? MatchTypeBoolean : NonNullable<TObject[TKey]> extends string ? MatchTypeText | MatchTypeInArray : NonNullable<TObject[TKey]> extends number ? MatchTypeNumber : NonNullable<TObject[TKey]> extends Array<unknown> ? MatchTypeArray : MatchTypeDefault > = { conditionType?: never; conditions?: never; fieldName: TKey; matchType: TMatchType; value: [TMatchType] extends [MatchTypeInArray] // 修改此处 ? string[] : NonNullable<TObject[TKey]>; orderBy: OrderParamsOrderBy<TObject>[]; };
正确解决方案
核心问题是原QueryParamsCondition的TMatchType为联合类型,导致条件判断生成分布式的联合类型。需针对每个matchType单独生成对应类型,再合并为联合:
步骤1:定义字段类型对应的匹配类型集合
type MatchTypeFor<T> = T extends boolean ? MatchTypeBoolean : T extends string ? MatchTypeText | MatchTypeInArray : T extends number ? MatchTypeNumber : T extends Array<unknown> ? MatchTypeArray : MatchTypeDefault;
步骤2:重构QueryParamsCondition
type QueryParamsCondition<TObject extends {}, TKey extends keyof TObject> = { [M in MatchTypeFor<NonNullable<TObject[TKey]>>]: { conditionType?: never; conditions?: never; fieldName: TKey; matchType: M; value: M extends MatchTypeInArray ? string[] : NonNullable<TObject[TKey]>; orderBy: OrderParamsOrderBy<TObject>[]; } }[MatchTypeFor<NonNullable<TObject[TKey]>>];
步骤3:保持GenericQuery不变
export type GenericQuery<TObject extends {}> = { [TKey in keyof TObject]: | QueryParamsTopLevel<TObject> | QueryParamsCondition<TObject, TKey>; }[keyof TObject];
验证效果
// 正确:matchType为'EQUALS'时,value必须是string const test1: GenericQuery<TestType> = { fieldName: 'id', matchType: 'EQUALS', value: '123', }; // 正确:matchType为'IN'时,value必须是string[] const test2: GenericQuery<TestType> = { fieldName: 'id', matchType: 'IN', value: ['123', '456'], }; // 错误:matchType为'EQUALS'时不能传入数组(TypeScript会报错) const test3: GenericQuery<TestType> = { fieldName: 'id', matchType: 'EQUALS', value: ['123'], };
内容的提问来源于stack exchange,提问作者fuddibacke
相关产品推荐
相关产品推荐

