如何优化TypeScript过滤器类型定义实现严格校验?
TypeScript 过滤器类型严格校验问题
现有类型定义
// 类型定义 export enum TableFilterType { MULTIPLE = 'multiple', SINGLE = 'single' } export type TableBasePrimaryFilter = { type: TableFilterType; id: string | number; label: string; plural: string; icon?: string; unit?: string; iconClass?: Array<string>; optionsListWidth?: number; }; export enum FilterDataLoadOrigin { CLIENT = 'CLIENT', SERVER = 'SERVER', } type TablePrimaryFilterCommon = { id: string | number; label: string; plural: string; type: TableFilterType; }; export type TableMultiplePrimaryFilterClient = TablePrimaryFilterCommon & { mode: FilterDataLoadOrigin.CLIENT; }; export type TableMultiplePrimaryFilterServer = TablePrimaryFilterCommon & { mode: FilterDataLoadOrigin.SERVER; hasMore: boolean; total: number; }; export type TableMultiplePrimaryFilter = TableMultiplePrimaryFilterClient | TableMultiplePrimaryFilterServer; export type TableSinglePrimaryFilterClient = TablePrimaryFilterCommon & { mode: FilterDataLoadOrigin.CLIENT; }; export type TableSinglePrimaryFilterServer = TablePrimaryFilterCommon & { mode: FilterDataLoadOrigin.SERVER; hasMore: boolean; total: number; }; export type TableSinglePrimaryFilter = TableSinglePrimaryFilterClient | TableSinglePrimaryFilterServer; export type TablePrimaryFilter = | TableMultiplePrimaryFilter | TableSinglePrimaryFilter
存在的校验漏洞
漏洞1:CLIENT模式下非法属性未触发报错
当mode为FilterDataLoadOrigin.CLIENT时,hasMore和total属于非法属性,但TypeScript未检测到错误:
// 示例 const x: TablePrimaryFilter = { id: 'level', label: 'label', plural: 'plural', type: TableFilterType.SINGLE, mode: FilterDataLoadOrigin.CLIENT, // 注意:以下两行应该触发错误,因为CLIENT模式下不允许hasMore和total hasMore: true, total: 100, };
漏洞2:新增NUMBER类型后非法属性未触发报错
新增NUMBER类型过滤器后,当type为TableFilterType.NUMBER时,mode、hasMore、total属于非法属性,但TypeScript未检测到错误:
export enum TableFilterType { MULTIPLE = 'multiple', SINGLE = 'single', NUMBER = 'number', } export type TableNumberPrimaryFilter = TablePrimaryFilterCommon & { type: TableFilterType.NUMBER }; export type TablePrimaryFilter = | TableMultiplePrimaryFilter | TableSinglePrimaryFilter | TableNumberPrimaryFilter
// 示例 const x: TablePrimaryFilter = { id: 'level', label: 'label', plural: 'plural', type: TableFilterType.NUMBER, // 注意:以下行应该触发错误,因为NUMBER类型不允许mode、hasMore和total mode: FilterDataLoadOrigin.SERVER, hasMore: true, total: 100, };
解决方案
要实现严格的属性校验,需要在联合类型的每个分支中显式排除不需要的属性,通过将这些属性的类型设置为never来禁止它们存在。修改后的类型定义如下:
export enum TableFilterType { MULTIPLE = 'multiple', SINGLE = 'single', NUMBER = 'number', } export enum FilterDataLoadOrigin { CLIENT = 'CLIENT', SERVER = 'SERVER', } type TablePrimaryFilterCommon = { id: string | number; label: string; plural: string; type: TableFilterType; // 公共可选属性 icon?: string; unit?: string; iconClass?: Array<string>; optionsListWidth?: number; }; // 多选择器 - CLIENT模式:禁止hasMore和total export type TableMultiplePrimaryFilterClient = TablePrimaryFilterCommon & { type: TableFilterType.MULTIPLE; mode: FilterDataLoadOrigin.CLIENT; hasMore?: never; total?: never; }; // 多选择器 - SERVER模式:必须有hasMore和total export type TableMultiplePrimaryFilterServer = TablePrimaryFilterCommon & { type: TableFilterType.MULTIPLE; mode: FilterDataLoadOrigin.SERVER; hasMore: boolean; total: number; }; export type TableMultiplePrimaryFilter = TableMultiplePrimaryFilterClient | TableMultiplePrimaryFilterServer; // 单选择器 - CLIENT模式:禁止hasMore和total export type TableSinglePrimaryFilterClient = TablePrimaryFilterCommon & { type: TableFilterType.SINGLE; mode: FilterDataLoadOrigin.CLIENT; hasMore?: never; total?: never; }; // 单选择器 - SERVER模式:必须有hasMore和total export type TableSinglePrimaryFilterServer = TablePrimaryFilterCommon & { type: TableFilterType.SINGLE; mode: FilterDataLoadOrigin.SERVER; hasMore: boolean; total: number; }; export type TableSinglePrimaryFilter = TableSinglePrimaryFilterClient | TableSinglePrimaryFilterServer; // 数字过滤器:禁止mode、hasMore和total export type TableNumberPrimaryFilter = TablePrimaryFilterCommon & { type: TableFilterType.NUMBER; mode?: never; hasMore?: never; total?: never; }; export type TablePrimaryFilter = | TableMultiplePrimaryFilter | TableSinglePrimaryFilter | TableNumberPrimaryFilter;
验证效果
现在再运行之前的错误示例,TypeScript会正确触发报错:
- CLIENT模式下添加
hasMore/total会提示类型不兼容 - NUMBER类型下添加
mode/hasMore/total会提示类型不兼容
这样就实现了严格的属性校验,确保每个过滤器类型只能包含允许的属性。
内容的提问来源于stack exchange,提问作者Rahul Badenkal
相关产品推荐
相关产品推荐

