当属性的类型是判别式时,为什么“typeof”不缩小联合类型?

hli*_*liu 6 type-inference discriminated-union narrowing typescript union-types

如果联合类型的成员共享一个属性,并且该属性的类型可用于区分这些成员,那么我应该能够使用作为条件来缩小if子句中的类型范围typeof。但这不起作用。

例如,在if下面的子句中, 的类型event应推断为UserTextEvent, 的类型event.target应推断为HTMLInputElement

type UserTextEvent = { value: string, target: HTMLInputElement };
type UserMouseEvent = { value: [number, number], target: HTMLElement };

type UserEvent = UserTextEvent | UserMouseEvent 


function handle(event: UserEvent) {
  if (typeof event.value === 'string') {
    event.value  // string, as expected

    event.target // should be narrowed to HTMLInputElement, but
                 // is still HTMLInputElement | HTMLElement. Why?
  }
}
Run Code Online (Sandbox Code Playgroud)

vas*_*vas 4

Typescript 的可区分联合(当前)仅支持属性作为判别式,而不支持属性类型

\n

以下是在 Typescript 2.0 中添加判别联合支持时支持的判别属性类型:

\n
\n

x.p == v判别式属性类型保护是、x.p === vx.p != v或形式的表达式x.p !== v,其中pv是字符串文字类型或字符串文字类型的联合的属性和表达式。判别属性类型保护将 x 的类型缩小为具有判别属性 p 和 v 的可能值之一的 x 的构成类型。

\n

请注意,我们目前仅支持字符串文字类型的判别属性。我们打算稍后添加对布尔和数字文字类型的支持。

\n
\n

Typescript 3.2 扩展了支持

\n
\n

联合的公共属性现在被视为判别式,只要它们包含某种单例类型(例如字符串文字、null 或未定义),并且它们不包含泛型。

\n

因此,TypeScript 3.2 将以下示例中的 error 属性视为判别式,而在此之前它不会\xe2\x80\x99t,因为 Error 是\xe2\x80\x99t 单例类型。因此,缩小功能可以在展开函数的主体中正常工作。

\n
type Result<T> = { error: Error; data: null } | { error: null; data: T };\nfunction unwrap<T>(result: Result<T>) {\n  if (result.error) {\n    // Here \'error\' is non-null\n    throw result.error;\n  }\n  // Now \'data\' is non-null\n  return result.data;\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

Typescript 4.5 添加了对“模板字符串类型作为判别式”的支持

\n
\n

TypeScript 4.5 现在可以缩小具有模板字符串类型的值的范围,并且还将模板字符串类型识别为判别式。

\n

例如,以下内容过去在 TypeScript 4.5 中失败,但现在成功进行了类型检查。

\n
export interface Success {\n    type: `${string}Success`;\n    body: string;\n}\n \nexport interface Error {\n    type: `${string}Error`;\n    message: string\n}\n \nexport function handler(r: Success | Error) {\n    if (r.type === "HttpSuccess") {\n        const token = r.body;\n                     \n(parameter) r: Success\n    }\n}Try\n
Run Code Online (Sandbox Code Playgroud)\n
\n

在所有情况下,用于区分的是判别属性的值,而不是其类型

\n

换句话说,从当前版本的 Typescript 4.5 开始,你就不走运了。只是(尚)不支持。以下是相关的未决问题:

\n\n

以下是Typescript 开发团队负责人 Ryan Cavanaugh 的评论

\n
\n

从表面上看,不幸的是,这个功能的实现比我们预期的要复杂得多,而且我们认为在这种情况下成本/效益比并不好。尽管我们非常想支持这种模式,但这些变化的影响感觉太深远了(即使它们对于实际支持该场景是必要的)。我们将保留最初的问题,以防稍后出现更简单的方法,但我们现在不愿意在关键代码路径中引入如此复杂的内容。

\n
\n