TypeScript函数联合类型报错原因及通用验证类型设计咨询
TypeScript验证函数类型问题解析
问题详情
我在理解验证函数的TypeScript类型时遇到了问题,具体情况如下:
- 基础类型与验证参数类型定义:
type ValidationFunctionType = { fieldName: string; fields: string; }; type MaxYearType = ValidationFunctionType & { fieldValue: Date; args: { maxYear: Date } }; type MinLengthType = ValidationFunctionType & { fieldValue: string; args: { minLength: number }; }; type MinNumberType = ValidationFunctionType & { fieldValue: number; args: { minNumber: number }; };
- 函数联合类型定义:
export type ValidationFunctionsType = | (({}: MinLengthType) => string) | (({}: MinNumberType) => string) | (({}: MaxYearType) => string);
- 报错场景:
当创建函数数组并通过map调用时,出现两个报错:fieldValue类型变为never,args要求同时满足所有交叉类型的属性。
我想了解:
- 为何
args和fieldValue的类型是交叉类型而非联合类型? - 如何为这些函数创建正确且更通用的类型?
更新:我尝试了将参数设为联合类型的函数定义,TypeScript可识别,但不确定是否为正确方案,因为每个函数需在开头验证参数类型。
问题解析与解决方案
一、为什么args和fieldValue变成交叉类型?
这是TypeScript中函数类型的逆变特性导致的:当你调用一个函数联合类型时,TypeScript无法确定你实际调用的是联合中的哪一个函数,因此会要求传入的参数必须能满足所有联合分支的参数类型,也就是将所有分支的参数类型做交叉处理(MinLengthType & MinNumberType & MaxYearType)。
对于fieldValue来说,string & number & Date没有交集,所以类型变成never;对于args来说,三个args类型交叉后,要求同时拥有minLength、minNumber、maxYear三个属性,这显然不符合需求,因此报错。
二、正确的类型定义方案
方案1:使用参数联合类型的单个函数类型(你尝试的方案)
将参数定义为所有验证类型的联合,然后函数类型统一为接收该联合参数的函数:
// 定义所有验证参数的联合类型 type ValidationParams = MinLengthType | MinNumberType | MaxYearType; // 统一的验证函数类型 export type ValidationFunctionsType = (params: ValidationParams) => string;
实现验证函数时,需要通过类型守卫区分不同的参数类型,这是TypeScript处理联合类型的标准方式,完全合理:
const minLengthValidator: ValidationFunctionsType = (params) => { // 通过args中的属性做类型守卫 if ('minLength' in params.args) { // 此处params会被自动推断为MinLengthType if (params.fieldValue.length < params.args.minLength) { return `${params.fieldName}长度不能小于${params.args.minLength}`; } } // 其他验证逻辑... return ''; }; const minNumberValidator: ValidationFunctionsType = (params) => { if ('minNumber' in params.args) { if (params.fieldValue < params.args.minNumber) { return `${params.fieldName}不能小于${params.args.minNumber}`; } } return ''; };
方案2:用泛型实现更通用的类型
如果需要更灵活、可扩展的定义,可以用泛型抽象基础结构,避免重复编写相似的类型:
// 基础验证参数类型 type BaseValidationParams = { fieldName: string; fields: string; }; // 泛型验证参数:指定字段值和参数的类型 type ValidationParams<Value, Args> = BaseValidationParams & { fieldValue: Value; args: Args; }; // 泛型验证函数类型 type ValidationFunction<Value, Args> = (params: ValidationParams<Value, Args>) => string; // 定义具体的验证函数类型 type MinLengthValidator = ValidationFunction<string, { minLength: number }>; type MinNumberValidator = ValidationFunction<number, { minNumber: number }>; type MaxYearValidator = ValidationFunction<Date, { maxYear: Date }>;
使用时,直接创建具体类型的函数数组即可,TypeScript会自动推断参数类型,无需额外类型守卫(每个函数只处理对应类型的参数):
const validators: (MinLengthValidator | MinNumberValidator | MaxYearValidator)[] = [ (params) => { // params自动推断为MinLengthValidator的参数 if (params.fieldValue.length < params.args.minLength) { return `${params.fieldName}长度不足`; } return ''; }, (params) => { // params自动推断为MinNumberValidator的参数 if (params.fieldValue < params.args.minNumber) { return `${params.fieldName}数值过小`; } return ''; } ];
内容的提问来源于stack exchange,提问作者yetanotherprogrammer
相关产品推荐
相关产品推荐

