【问题标题】:User defined type guards: do I need to check member types?用户定义类型保护:我需要检查成员类型吗?
【发布时间】:2020-09-12 05:58:26
【问题描述】:

我是 TypeScript 的新手,并试图了解接口和类型保护。假设我有一个描述客户端和服务器之间传递的 JSON 的接口:

interface Player {
  name: string
  score: number
}

我第一次尝试为此编写类型保护是:

function isPlayer (value: any): value is Player {
  const { name, score } = (value as Player)
  return typeof name == 'string' && typeof score == 'number'
}

我的 IDE 警告我 typeof 检查是多余的:'name' always has type 'string' 等。相反,按照 TypeScript 手册和其他地方的示例,我只需要检查没有未定义的成员:

function isPlayer (value: any): value is Player {
  const { name, score } = (value as Player)
  return name !== undefined && score !== undefined
}

这真的足够了吗?我的第一个想法是关于类型断言value as Player 保证其成员的类型。但是手册本身suggests not

类型断言是告诉编译器“相信我,我知道我在做什么”的一种方式。类型断言类似于其他语言中的类型转换,但不执行数据的特殊检查或重组。它没有运行时影响,并且纯粹由编译器使用。 TypeScript 假定您(程序员)已经执行了您需要的任何特殊检查。

A quick experiment 似乎支持这一点。我的isPlayer 类型守卫的宽松形式将允许score 是字符串的对象通过。既然是这种情况,我很困惑为什么我看到的关于类型保护的手册和每个教程都只提到了未定义检查的简单形式。

假设我有以下情况:

enum Stage {
  WaitingForCard = 'Waiting for card',
  PlacingBets = 'Placing bets',
  Scoring = 'Scoring',
}

interface Turn {
  stage: Stage
  players: string[]
}

看起来真正安全的类型保护必须是这样的:

function isRound (v: any): v is Round {
  const { stage, players } = (v as Round)

  if (stage != Stage.WaitingForCard || stage != Stage.PlacingBets || stage != Stage.Scoring) {
    return false
  }

  return players.every(player => typeof player == 'string')
}

这似乎是不可能的,因为编译器会抱怨详尽的 if 条件。有什么可以让我摆脱这种痛苦吗? 我是否只需要在接口和类型保护方面接受一定程度的类型不安全?

【问题讨论】:

  • 断言不保证类型安全。相反。你告诉编译器闭嘴,因为你知道你在用断言做什么。您的 IDE 在此特定上下文中是错误的,因为它是多余的。也就是说,你可能会有点......过度使用你的类型警卫?类型保护对于您无法控制的事情或在可能有多种类型的情况下很重要,即区分可能类型的联合。如果您控制 API,则为每个 API 响应编写类型保护是过度的 IMO。
  • 我想你是对的,作为惯用的 TypeScript,它是矫枉过正的。我来自最近涉足 Elm 的严格类型系统,您可以在其中解码任何传入的 JSON 以保证其形状。我希望通过 TS 可以有类似的体验。绝对类型安全有时似乎有点过头了......直到你被错误所困扰。
  • 我曾开发过很多企业 TS 应用程序,但没有人曾经对每个 API 响应进行类型检查。开发前端的一部分是在更新 API 时维护 API 响应的接口。许多商店使用类型生成器来自动化这部分。您需要能够稍微依赖自己的代码。所有这些类型保护甚至可能无法防止错误,具体取决于您编写和使用它们的方式。 TS 中的类型安全是对 JS 的增量改进,但它本质上仍然是一种松散类型的语言,您可以在其中对类型做任何事情。

标签: typescript typeguards


【解决方案1】:

TypeScript 允许你对你的类型保护有点懒惰,并且不会强迫你检查所有(或实际上任何)属性,你可以根据需要尽可能精确或不精确(如果你使用 ‛unknown‛它变得有点困难)。然后代码存在类型更改和类型保护被遗忘的问题,导致运行时错误:(

我遇到了类似的问题,需要手动定义我的类型保护,所以我创建了一个 TypeScript transformer 可以自动为我完成。例如在你的情况下:

import { isA } from 'ts-type-checked'; 

if (isA<Player>(value)) {
  // you are sure value is a Player here
}

您可以在NPM page 上找到有关如何在您的项目中使用它的指南。

免责声明:ofc 我是图书馆的作者 :)

【讨论】:

  • 这是一个非常酷的库!我希望 TS 拥有的那种功能。我会调查的。关于类型和类型保护之间的漂移也是一个很好的观点。
猜你喜欢
  • 2019-01-03
  • 2015-12-19
  • 2021-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-11
  • 2016-09-06
相关资源
最近更新 更多