关于javascript:TypeScript-运行时类型检查指南

37次阅读

共计 3033 个字符,预计需要花费 8 分钟才能阅读完成。

mistermicheels 原作,受权 New Frontend 翻译。

为什么须要额定的类型查看?

TypeScript 只在编译期执行动态类型查看!理论运行的是从 TypeScript 编译的 JavaScript,这些生成的 JavaScript 对类型无所不知。编译期动态类型查看在代码库外部能施展很大作用,但对不合标准的输出(比方,从 API 处接管的输出)无能为力。

运行时查看的严格性

  • 至多须要和编译期查看一样严格,否则就失去了编译期查看提供的保障。
  • 如有必要,能够比编译期查看更严格,例如,年龄须要大于等于 0。

运行时类型查看策略

定制代码手动查看

  • 灵便
  • 可能比拟干燥,容易出错
  • 容易和理论代码脱节

应用校验库手动查看

比方应用 joi:

import Joi from "@hapi/joi"const schema = Joi.object({firstName: Joi.string().required(),    lastName: Joi.string().required(),    age: Joi.number().integer().min(0).required()});
  • 灵便
  • 容易编写
  • 容易和理论代码脱节

手动创立 JSON Schema

例如:

{"$schema": "http://json-schema.org/draft-07/schema#",  "required": [    "firstName",    "lastName",    "age"],  "properties": {"firstName": {      "type": "string"},    "lastName": {"type": "string"},    "age": {"type": "integer",      "minimum": 0}  }}
  • 应用规范格局,有大量库能够校验。
  • JSON 很容易存储和复用。
  • 可能会很简短,手写 JSON Schema 可能会很干燥。
  • 须要确保 Schema 和代码同步更新。

主动创立 JSON Schema

  • 基于 TypeScript 代码生成 JSON Schema
    — 比方 typescript-json-schema 这个工具就能够做到这一点(同时反对作为命令行工具应用和通过代码调用)。
    — 须要确保 Schema 和代码同步更新。
  • 基于 JSON 输出示例生成
    — 没有应用曾经在 TypeScript 代码中定义的类型信息。
    — 如果提供的 JSON 输出示例和理论输出不统一,可能导致谬误。
    — 依然须要确保 Schema 和代码同步更新。

转译

例如应用 ts-runtime。

这种形式会将代码转译成性能上等价但内置运行时类型查看的代码。

比方,上面的代码:

interface Person {firstName: string;    lastName: string;    age: number;}const test: Person = {firstName: "Foo",    lastName: "Bar",    age: 55}

会被转译为:

import t from "ts-runtime/lib";const Person = t.type("Person",    t.object(        t.property("firstName", t.string()),        t.property("lastName", t.string()),        t.property("age", t.number())    ));const test = t.ref(Person).assert({firstName: "Foo",    lastName: "Bar",    age: 55});

这一形式的缺点是无法控制在何处进行运行时查看(咱们只需在输入输出的边界处进行运行时类型查看)。

顺便提一下,这是一个实验性的库,不倡议在生产环境应用。

运行时类型派生动态类型

比方应用 io-ts 这个库。

这一形式下,咱们定义运行时类型,TypeScript 会依据咱们定义的运行时类型推断出动态类型。

运行时类型示例:

import t from "io-ts";const PersonType = t.type({firstName: t.string,  lastName: t.string,  age: t.refinement(t.number, n => n >= 0, 'Positive')})

从中提取相应的动态类型:

interface Person extends t.TypeOf<typeof PersonType> {}

以上类型等价于:

interface Person {firstName: string;    lastName: string;    age: number;}
  • 类型总是同步的。
  • io-ts 很弱小,比方反对递归类型。
  • 须要将类型定义为 io-ts 运行时类型,这在定义类时不实用:
    — 有一种变通的方法是应用 io-ts 定义一个接口,而后让类实现这个接口。然而,这意味着每次给类减少属性的时候都要更新 io-ts 类型。
  • 不容易复用接口(比方前后端之间应用同一接口),因为这些接口是 io-ts 类型而不是一般的 TypeScript 类型。

基于装璜器的类校验

比方应用 class-validator 这个库。

  • 基于类属性的装璜器。
  • 和 Java 的 JSR-380 Bean Validation 2.0(比方 Hibernate Validator 就实现了这一规范)很像。
    — 此类 Java EE 格调的库还有 typeorm(ORM 库,相似 Java 的 JPA)和 routing-controllers(用于定义 API,相似 Java 的 JAX-RS)。

代码示例:

import {plainToClass} from "class-transformer";import {validate, IsString, IsInt, Min} from "class-validator";class Person {@IsString()    firstName: string;    @IsString()    lastName: string;    @IsInt()    @Min(0)    age: number;}const input: any = {firstName: "Foo",    age: -1};const inputAsClassInstance = plainToClass(Person, input as Person);validate(inputAsClassInstance).then(errors => {    // 错误处理代码});
  • 类型总是同步的。
  • 须要对类进行查看时很有用。
  • 能够用来查看接口(定义一个实现接口的类)。

留神:class-validator 用于具体的类实例。在下面的代码中,咱们应用它的姊妹库 class-transformer 将一般输出转换为 Person 实例。转换过程自身不进行任何类型查看。

Photo by John Schnobrich on Unsplash

正文完
 0