将类型谓词与断言函数结合使用

前端开发 2026-07-09

我正在使用ajv将一个对象对照JSON模式进行校验。简化后的代码如下:

interface ValidateFunction<T = unknown> {
    (data: unknown): data is T;
}

declare const test: ValidateFunction<string>;

declare function assertIsTrue(b: boolean): asserts b is true;

function f(s: unknown): string {
  const isValid = test(s);
  assertIsTrue(isValid);
  return s;
}

在线演练

我在 return 上遇到了一个错误:

类型 'unknown' 不能赋值给类型 'string'。

我本来以为这段代码可以工作。如果把 assertIsTrue 改成 if (!valid) throw new Error();,编译器就不会报错。这是个bug吗?

解决方案

这是 microsoft/TypeScript#60904 的主题,被归类为“不是缺陷”的问题,意味着它不被视为一个bug,但至多是一个功能缺失。

你正在尝试将以下两者结合起来:

  • [别名条件的控制流分析] 的实现见 [microsoft/TypeScript#44730],以及
  • [断言函数] 的实现见 [microsoft/TypeScript#32695]。

但这对像 asserts b is true 这样的断言谓词并不起作用。这样的断言谓词只作用于值 b 本身,并且其行为与若直接对 b 进行控制流分析时的结果不完全相同。请参阅实现者的注释 [https://github.com/microsoft/TypeScript/pull/32695#issuecomment-517937626]。

因此,当你写出 assertIsTrue(isValid) 时,它会将 isValid 收窄为 true,但不会触发对条件别名 isValid 的收窄。

作为变通办法,不使用 asserts b is true,你可以使用 asserts b,在直接对 b 进行真值性检查时,它的行为确实与控制流分析完全一致。并非总能用这种方式重构谓词,但由于布尔值 b 只有在等于 true 时才为真,因此足矣:

declare function assertIsTrue(b: boolean): asserts b;

function f(s: unknown): string {
  const isValid = test(s);
  assertIsTrue(isValid);  
  return s; // okay
}

也有可能提交一个功能请求,允许 asserts x is trueasserts x is falsex 为别名条件的情况下按你期望的方式工作,但扩展对控制流分析的行为可能成本高昂。若某段原本不会触发更多分析的代码现在却会触发分析,这种分析可能会进一步触发分析,等等。因此,即便增加额外分析可能有用,也并非总是可行或值得。这并不意味着不应提交此类请求,只是除非在复杂性和性能方面显然值得实现,否则我不指望它会被实现。

代码的Playground链接

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章