如何通过Jest断言优雅实现TypeScript类型收窄?
问题描述
在编写TypeScript+Jest测试时遇到类型收窄问题:已通过Jest断言result.isLoading为false,但TypeScript无法识别该断言结果,仍需手动通过抛错收窄类型,想找更简洁的替代方式。
代码示例:
function getData(): { isLoading: true } | { isLoading: false; data: string } { return { isLoading: false, data: "hello world", }; } describe(getData, () => { it("behaves correctly", () => { const result = getData(); expect(result.isLoading).toBe(false); // 尽管已经断言isLoading为false,TypeScript仍不识别,需手动抛错收窄类型 if (result.isLoading) { throw new Error("expected isLoading to be false"); } expect(result.data).toBe("hello world"); }); });
解决方案
以下几种简洁方式可避免手动抛错的冗余代码:
1. 类型断言配合expect.assertions()
直接用类型断言告诉TypeScript当前result的具体类型,同时用expect.assertions()确保前置断言已执行,避免类型断言的风险:
describe(getData, () => { it("behaves correctly", () => { const result = getData(); expect.assertions(2); // 保证两个断言都会执行 expect(result.isLoading).toBe(false); // 类型断言收窄后直接访问data属性 expect((result as { isLoading: false; data: string }).data).toBe("hello world"); }); });
2. 封装带断言的类型守卫
写一个自定义类型守卫,把Jest断言逻辑封装进去,一次调用同时完成测试验证和类型收窄:
function isLoaded(result: ReturnType<typeof getData>): result is { isLoading: false; data: string } { expect(result.isLoading).toBe(false); return !result.isLoading; } describe(getData, () => { it("behaves correctly", () => { const result = getData(); if (isLoaded(result)) { // 此处TypeScript已自动识别result的类型 expect(result.data).toBe("hello world"); } }); });
这种方式逻辑复用性强,后续其他测试用例也能直接调用该类型守卫。
3. 使用Jest属性断言辅助类型推断
用expect(result).toHaveProperty('isLoading', false)代替普通的布尔断言,部分场景下TypeScript能基于这个断言自动收窄类型:
describe(getData, () => { it("behaves correctly", () => { const result = getData(); expect(result).toHaveProperty('isLoading', false); // 此时TypeScript可识别result.isLoading为false,能直接访问data expect(result.data).toBe("hello world"); }); });
注意:这种方式的类型推断依赖TypeScript对Jest断言的类型支持,稳定性略逊于前两种方法。
内容的提问来源于stack exchange,提问作者dwjohnston
相关产品推荐
相关产品推荐

