TypeScript如何从类型中移除值为undefined/null的属性?
TypeScript 过滤类型中值为 undefined/null 的属性及请求函数实现
一、创建过滤后的类型
要移除类型中值为 undefined 或 null 的属性,可以通过映射类型+条件判断筛选出符合要求的键,再构造新类型:
方式1:Pick 筛选有效属性
// 通用工具类型:过滤掉值为 undefined 或 null 的属性 type FilterUndefinedNullProps<T> = Pick<T, { [K in keyof T]: T[K] extends undefined | null ? never : K }[keyof T]>; // 原类型 type TEndpointsPayload = { getList: undefined, open: { id: string }, close: { id: string } }; // 生成目标类型:自动移除 getList type TEndpointsNonEmptyPayload = FilterUndefinedNullProps<TEndpointsPayload>;
方式2:Omit 排除无效属性
也可以先找出需要排除的键,再用 Omit 移除:
// 通用工具类型:找出值为 undefined 或 null 的属性键 type KeysWithUndefinedNull<T> = { [K in keyof T]: T[K] extends undefined | null ? K : never }[keyof T]; // 生成目标类型 type TEndpointsNonEmptyPayload = Omit<TEndpointsPayload, KeysWithUndefinedNull<TEndpointsPayload>>;
两种方式最终都会得到你期望的 TEndpointsNonEmptyPayload,属性 getList 会被彻底移除,而非设为 never。
二、请求函数的实现方案
方案1:函数重载
通过重载区分「需要传payload」和「不需要传payload」的场景,类型检查更清晰:
// 先定义过滤后的类型(复用上面的工具类型) type FilterUndefinedNullProps<T> = Pick<T, { [K in keyof T]: T[K] extends undefined | null ? never : K }[keyof T]>; type TEndpointsPayload = { getList: undefined, open: { id: string }, close: { id: string } }; type TEndpointsNonEmptyPayload = FilterUndefinedNullProps<TEndpointsPayload>; // 提取不需要传payload的接口键 type TEndpointsEmptyPayload = Exclude<keyof TEndpointsPayload, keyof TEndpointsNonEmptyPayload>; class ApiService { // 重载1:需要传入payload的接口 private request<T extends keyof TEndpointsNonEmptyPayload>(endpoint: T, payload: TEndpointsNonEmptyPayload[T]): void; // 重载2:不需要传入payload的接口 private request<T extends TEndpointsEmptyPayload>(endpoint: T): void; // 实际实现逻辑 private request(endpoint: keyof TEndpointsPayload, payload?: unknown): void { // 这里编写请求逻辑,比如根据endpoint处理payload if (payload) { console.log(`请求 ${endpoint},参数:`, payload); } else { console.log(`请求 ${endpoint}`); } } // 测试调用(类型检查生效) test() { this.request('getList'); // ✅ 正确,无需传参 this.request('open', { id: 'user-123' }); // ✅ 正确,必须传参 // this.request('open'); // ❌ 报错:缺少payload参数 // this.request('getList', {}); // ❌ 报错:多余的参数 } }
方案2:条件类型+剩余参数
无需额外定义多个类型,直接在函数参数中用条件类型动态判断是否需要传入payload:
type TEndpointsPayload = { getList: undefined, open: { id: string }, close: { id: string } }; class ApiService { private request<T extends keyof TEndpointsPayload>( endpoint: T, // 条件判断:如果属性值是undefined/null,则不需要传参;否则必须传对应类型的payload ...args: TEndpointsPayload[T] extends undefined | null ? [] : [TEndpointsPayload[T]] ): void { const payload = args[0]; console.log(`请求 ${endpoint}`, payload); } // 测试调用 test() { this.request('getList'); // ✅ 正确 this.request('open', { id: 'user-456' }); // ✅ 正确 // this.request('open'); // ❌ 报错:缺少必要参数 // this.request('getList', {}); // ❌ 报错:多余参数 } }
三、为什么直接用 Omit/Exclude 没生效?
你之前尝试的 Omit/Exclude 没成功,是因为没有正确筛选出需要排除的键。直接使用 Exclude<keyof TEndpointsPayload, ...> 时,需要先明确哪些键对应的值是 undefined/null——上面的工具类型就是帮你完成这一步:先通过映射类型标记出无效键为 never,再提取这些键进行排除,才能彻底移除属性。
内容的提问来源于stack exchange,提问作者Itay Elkouby
相关产品推荐
相关产品推荐

