如何让TypeScript类型守卫同时实现类型收窄与代码提示?
问题:实现兼具类型收窄与代码提示的
assertIsDefined类型守卫 问题背景
我需要实现一个带预期值的assertIsDefined类型守卫,要求同时达成两个目标:
- 断言成功后,将变量类型精准收窄为指定的预期值
- 输入预期值参数时,能获得对应的代码智能补全提示
当前遇到矛盾:给泛型添加| unknown可实现类型收窄,但会丢失代码提示;不添加则能保留提示,却无法完成类型收窄。
两种现有实现的测试表现
{ const value_number1 = 123 as 3 | 123 | 777 | undefined; // 😪 错误表现:删除', 123'中的'123'后按Ctrl+Space无补全提示 assertIsDefined_withDetailedType(value_number1, 123); // 🌈 正确表现:value_number1的类型被收窄为123 console.log(value_number1); // value_number1: 123 } { const value_number2 = 123 as 3 | 123 | 777 | undefined; // 🌈 正确表现:删除', 123'中的'123'后按Ctrl+Space显示提示:[123, 3, 777] assertIsDefined_withSuggestions(value_number2, 123); // 😪 错误表现:value_number2的类型仍为3 | 123 | 777 console.log(value_number2); // value_number2: 3 | 123 | 777 } // 可实现类型收窄,但丢失代码提示 function assertIsDefined_withDetailedType<T>(value: T | unknown | null | undefined, expected: T): asserts value is T { if (value == null || expected !== value) { throw new TypeError(`value should be defined and equal expected value`); } } // 有代码提示,但无法完成类型收窄 function assertIsDefined_withSuggestions<T>(value: T | null | undefined, expected: T): asserts value is T { if (value == null || expected !== value) { throw new TypeError(`value should be defined and equal expected value`); } }
期望效果
{ const value_number2 = 123 as 3 | 123 | 777 | undefined; // 🌈 正确表现:删除', 123'中的'123'后按Ctrl+Space显示提示:[123, 3, 777] assertIsDefined_withAllWeWant(value_number2, 123); // 🌈 正确表现:value_number2的类型被收窄为123 console.log(value_number2); // value_number2: 123 } // 理想的类型守卫结构 function assertIsDefined_withAllWeWant(value: /* 此处应如何编写? */, expected: /* 此处应如何编写? */): asserts value is /* 此处应如何编写? */ { if (value == null || expected !== value) { throw new TypeError(`value should be defined and equal expected value`); } }
解决方案
通过引入两个泛型参数,分别约束原始值类型和预期值类型,即可同时满足两种需求:
function assertIsDefined_withAllWeWant<V, T extends V>( value: V | null | undefined, expected: T ): asserts value is T { if (value == null || value !== expected) { throw new TypeError(`value should be defined and equal expected value`); } }
实现原理
- 泛型
V用于捕获原始值的完整类型(包含null/undefined) - 泛型
T被约束为V的子类型,确保预期值是原始值可能的取值之一 - 断言语句
asserts value is T会将变量类型精准收窄为指定的预期值T - 由于
T继承自V,TypeScript能自动从V中提取非空成员作为补全提示
测试验证
使用上述函数后,既能在输入预期值时获得智能补全,又能在断言成功后将变量类型收窄为指定值,完全符合需求。
内容的提问来源于stack exchange,提问作者Egor
相关产品推荐
相关产品推荐

