如何使用TypeScript的is关键字收窄泛型类型?
解决TypeScript泛型类类型替换而非交集的问题
你的问题出在TypeScript对普通类型守卫的处理逻辑上:当你用this is Foo<B>作为返回类型时,TypeScript会将原有泛型类型T与目标类型B取交集,因为它认为这是对原有类型的细化而非替换。要实现直接替换类型的效果,你需要使用**断言函数(asserts)**来替代普通类型守卫。
解决方案:使用asserts类型断言
将类中的is方法返回类型改为asserts this is Foo<B>,这样TypeScript会直接将this的类型替换为Foo<B>,而不是生成交集类型。
示例代码:
class Foo<T> { value!: T; // 使用asserts替代this is ... is = <B>(): asserts this is Foo<B> => { // 这里需要添加实际的运行时检查逻辑,确保value符合B类型 // 示例中简化为直接通过,实际场景要根据需求判断 return; }; } const foo = new Foo<{ a: 'b', c: 'd' }>(); foo.value // { a: 'b', c: 'd' } if (foo.is<{ c: 'd', e: 'f' }>()) { foo.value // 类型为 { c: 'd', e: 'f' },符合预期 }
针对Vehicle/Motorcycle场景的实际应用
结合你提到的车辆类型判断场景,我们可以写更严谨的代码,包含运行时检查:
type Vehicle = { wheels: number }; type Motorcycle = Vehicle & { type: 'motorcycle', hasSidecar: boolean }; class VehicleContainer<T extends Vehicle> { value!: T; constructor(value: T) { this.value = value; } // 专门的Motorcycle类型断言方法 assertIsMotorcycle(): asserts this is VehicleContainer<Motorcycle> { // 运行时检查:确保value包含motorcycle的特征 const vehicle = this.value as unknown; if ( typeof vehicle !== 'object' || vehicle === null || (vehicle as any).type !== 'motorcycle' || typeof (vehicle as any).hasSidecar !== 'boolean' ) { throw new Error('当前车辆不是摩托车类型'); } } } // 测试普通车辆 const car = new VehicleContainer<Vehicle>({ wheels: 4 }); car.value; // 类型为Vehicle try { car.assertIsMotorcycle(); car.value; // 这里不会执行,因为断言会抛出错误 } catch (err) { console.log(err.message); // 输出:当前车辆不是摩托车类型 } // 测试摩托车 const motorcycle = new VehicleContainer<Motorcycle>({ wheels: 2, type: 'motorcycle', hasSidecar: false }); motorcycle.assertIsMotorcycle(); motorcycle.value; // 类型为Motorcycle,已成功替换
关键说明
asserts语句会直接修改变量的类型,而非细化原有类型,因此不会产生交集。- 必须添加对应的运行时检查逻辑,否则会导致类型不安全(TypeScript会信任你的断言,但运行时可能不符合类型定义)。
内容的提问来源于stack exchange,提问作者Morten Olsen
相关产品推荐
相关产品推荐

