Проблема

В последней статье я обсуждал использование runtypes для подключения статических типов и проверки во время выполнения. Теория, лежащая в основе runtypes, состоит в том, что у runtypes есть собственный язык определений, который может преобразовываться в функцию проверки во время выполнения и тип статического машинописного текста. Делая это, мы можем удалить дублирование в двух мирах.

Это здорово, однако один из самых больших минусов этого подхода: runtypes определен хороший язык определений, но он малоизвестен. Из-за этого невозможно создать сильную экосистему для разных сценариев.

Например, недавно я работал над проектом REST API, и мы используем открытый API для создания интерактивного документа. Однако нам приходится писать машинописные типы для этих запросов и ответов, что раздражает и очень легко сделать ошибку. Я предполагаю, что у нас мог бы быть какой-то инструмент, который мог бы генерировать эти типы машинописных текстов на основе открытого документа API, точно так же, как это делает graphql-codegen для схемы GraphQL.

Потому что открытый стандарт API зависит от схемы JSON, хорошо известного стандарта. Если мы сможем подключить статические типы и проверку во время выполнения на основе схемы JSON, я думаю, многие проблемы будут решены.

Другое решение

И это возможно!

Давайте посмотрим на пример файла схемы JSON с именем person.json:

{
  “title”: “Person”,
  “type”: “object”,
  “properties”: {
    “firstName”: {
      “type”: “string”
    },
    “lastName”: {
      “type”: “string”
    },
    “age”: {
      “description”: “Age in years”,
      “type”: “integer”,
      “minimum”: 0
    },
    “hairColor”: {
      “enum”: [“black”, “brown”, “blue”],
      “type”: “string”
    }
  },
  “additionalProperties”: false,
  “required”: [“firstName”, “lastName”]
}

Все, возможно, знают, что уже существует множество валидаторов схемы JSON, например: ajv, которые могут проверять передаваемые данные по заданной схеме JSON. Давайте приведем приведенный выше пример для проверки объекта:

import Ajv, { JSONSchemaType, DefinedError } from "ajv";
const validateObj = (obj: unknown) => {
  const ajv = new Ajv();
  const validate = ajv.compile({
    title: "Example Schema",
    type: "object",
    properties: {
      firstName: {
        type: "string"
      },
      lastName: {
        type: "string"
      },
      age: {
        description: "Age in years",
        type: "integer",
        minimum: 0
      },
      hairColor: {
        enum: ["black", "brown", "blue"],
        type: "string"
      }
    },
    additionalProperties: false,
    required: ["firstName", "lastName"]
  });
  if (validate(obj)) {
    // data is MyData here
    console.log("It is a valid obj: ", JSON.stringify(obj));
  } else {
    console.log("It is not a valid obj: ", JSON.stringify(obj));
    for (const err of validate.errors as DefinedError[]) {
      console.log(err);
    }
  }
};
validateObj({});
validateObj({
  firstName: "luke",
  lastName: "skywalker",
  age: 16,
  hairColor: "black"
});

И вывод ниже:

It is not a valid obj: {} {keyword: "required", dataPath: "", schemaPath: "#/required", params: Object, message: "should have required property 'firstName'"} It is a valid obj: {"firstName":"luke","lastName":"skywalker","age":16,"hairColor":"black"}

И есть еще один инструмент под названием json-schema-to-typescript, который может генерировать машинописный текст на основе схемы JSON.

Например, учитывая приведенную выше схему JSON, выполните следующую команду:

yarn add json-schema-to-typescript json2ts person.json > person.d.ts

Он сгенерирует следующий машинописный файл:

export interface Person {
  firstName: string;
  lastName: string;
  /**
   * Age in years
   */
  age?: number;
  hairColor?: "black" | "brown" | "blue";
}

Подвести итог

В этой статье мы используем другой подход для соединения проверки в реальном времени и статического машинописного типа с использованием более стандартной схемы JSON. Проверьте следующую диграмму для более подробной информации:

Первоначально опубликовано на https://github.com.