Подтвердить что ты не робот

Проверьте, реализует ли объект интерфейс во время выполнения с помощью TypeScript

Я загружаю файл конфигурации JSON во время выполнения и использую интерфейс для определения его ожидаемой структуры:

interface EngineConfig {
    pathplanner?: PathPlannerConfig;
    debug?: DebugConfig;
    ...
}

interface PathPlannerConfig {
    nbMaxIter?: number;
    nbIterPerChunk?: number;
    heuristic?: string;
}

interface DebugConfig {
    logLevel?: number;
}

...

Это упрощает доступ к различным свойствам, поскольку я могу использовать автозаполнение и т.д.

Вопрос: есть ли способ использовать это объявление, чтобы проверить правильность загружаемого файла? т.е. что у меня нет неожиданных свойств?

4b9b3361

Ответ 1

Нет.

В настоящее время типы используются только во время разработки и компиляции. Информация о типе никак не переводится в скомпилированный код JavaScript.

С fooobar.com/questions/290691/..., как указано @JasonEvans

С июня 2015 года существует открытая проблема об этом в репозитории TypeScript: https://github.com/microsoft/TypeScript/issues/3628

Ответ 2

Есть способ "есть", но вы должны реализовать его самостоятельно. Он называется "User Defined Type Guard" и выглядит так:

interface Test {
    prop: number;
}

function isTest(arg: any): arg is Test {
    return arg && arg.prop && typeof(arg.prop) == 'number';
}

Конечно, фактическая реализация функции isTest полностью зависит от вас, но хорошая isTest в том, что она является реальной функцией, что означает ее тестируемость.

Теперь во время выполнения вы должны использовать isTest() для проверки, уважает ли объект интерфейс. Во время компиляции машинопись набирает обороты и обрабатывает последующее использование, как ожидается, то есть:

let a:any = { prop: 5 };

a.x; //ok because here a is of type any

if (isTest(a)) {
    a.x; //error because here a is of type Test
}

Более подробные объяснения здесь: https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html

Ответ 3

Да. Вы можете выполнить эту проверку во время выполнения, используя расширенную версию компилятора TypeScript, которую я выпустил несколько лет назад. Вы можете сделать что-то вроде следующего:

export interface Person {
    name: string;
    surname: string;
    age: number;
}

let personOk = { name: "John", surname: "Doe", age: 36 };
let personNotOk = { name: 22, age: "x" };

// YES. Now you CAN use an interface as a type reference object.
console.log("isValid(personOk):  " + isValid(personOk, Person) + "\n");
console.log("isValid(personNotOk):  " + isValid(personNotOk, Person) + "\n");

и это результат:

isValid(personOk):  true

Field name should be string but it is number
isValid(personNotOk):  false

Обратите внимание, что функция isValid работает рекурсивно, поэтому вы можете использовать ее для проверки вложенных объектов. Вы можете найти полный рабочий пример здесь

Ответ 4

Я подозреваю, что TypeScript (мудро) придерживается Curly Law, а TypeScript является транспилером, а не средством проверки объектов. Тем не менее, я также считаю, что интерфейсы TypeScript могли бы сделать для паршивого проверки объектов, поскольку интерфейсы имеют (чудесно) ограниченный словарный запас и не могут проверять формы, которые другие программисты могут использовать для различения объектов, таких как длина массива, количество свойства, свойства рисунка и т.д.

При использовании объектов из кода не typescript я использую пакет проверки JSONSchema, например AJV, для проверки выполнения и генератора файлов .d.ts(например, DTSgenerator или DTS-generator), чтобы скомпилировать определения типа TypeScript из моего JSONshcema.

Основное предостережение состоит в том, что, поскольку JSONschemata способны описывать фигуры, которые невозможно отличить от TypeScript (например, patternProperties), это а не взаимно однозначный перевод из схемы JSON в .t.ds, и вам, возможно, придется вручную редактировать созданные .d.ts файлы при использовании таких схем JSON.

Тем не менее, поскольку другие программисты могут использовать такие свойства, как длина массива, чтобы вывести тип объекта, у меня есть привычка различать типы, которые могут быть смущены компилятором TypeScript, используя перечисление, чтобы предотвратить транспилеру от принятия использования одного введите вместо другого, например:

[MyTypes.yaml]

definitions: 
    type-A: 
        type: object
        properties:
            type:
                enum:
                - A
            foo: 
                type: array
                item: string
                maxLength: 2
    type-B: 
        type: object
        properties:
            type:
                enum:
                - B
            foo: 
                type: array
                item: string
                minLength: 3
        items: number

Создает файл .d.ts следующим образом:

[MyTypes.d.ts]

interface typeA{
    type: "A";
    foo: string[];
}

interface typeB{
    type: "B";
    foo: string[];
}

Ответ 5

Вот еще одна альтернатива, специально для этого:

ts-interface-builder - это инструмент, который вы запускаете во время сборки файла TypeScript (например, foo.ts) для создания дескрипторов времени выполнения (например, foo-ti.ts).

ts-interface-checker использует их для проверки объектов во время выполнения. Например

import {createCheckers} from 'ts-interface-checker';
import fooDesc from 'foo-ti.ts';
const checkers = createCheckers(fooDesc);

checkers.EngineConfig.check(someObject);   // Succeeds or throws an informative error
checkers.PathPlannerConfig.check(someObject);

Вы можете использовать strictCheck() чтобы гарантировать отсутствие неизвестных свойств.

Ответ 6

Здесь хороший способ. Вы можете преобразовать интерфейс TypeScript в схему JSON, используя typescript-json-schema, например

typescript-json-schema --required --noExtraProps \
  -o YOUR_SCHEMA.json YOUR_CODE.ts YOUR_INTERFACE_NAME

Затем проверьте данные во время выполнения, используя валидатор схемы JSON, такой как ajv, например

const fs = require('fs');
const Ajv = require('ajv');

// Load schema
const schema = JSON.parse(fs.readFileSync('YOUR_SCHEMA.json', {encoding:"utf8"}));
const ajv = new Ajv();
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
var validator = ajv.compile(schema);

if (!validator({"hello": "world"})) {
  console.log(validator.errors);
}

Ответ 7

Я не знаю, как выглядит ваш файл конфигурации, но наиболее очевидным будет json файл, хотя я бы пошел с json-схемой, чтобы проверить, соответствует ли файл схеме или нет.

Здесь json schema v4 documentation: http://json-schema.org/documentation.html

И один из примеров, как вы могли бы протестировать его: https://github.com/fge/json-schema-validator

Конечно, вы должны написать свою схему на основе интерфейсов, но вы не можете использовать их напрямую.

Ответ 8

да, есть lib, который делает это https://github.com/gcanti/io-ts

идея проста, имеет простые проверки свойств, сложенных в более сложные проверки объектов

Ответ 9

Вы можете использовать проверку класса

  1. Замените интерфейс на класс.
    class Cat {
        @IsNotEmpty() name: string;
    }

    // Static typing is work !!!
    const cat: Cat = { 
        name: "Barsik"
    };

  1. Создать функцию проверки. Пример:
    import { validateSync } from "class-validator";

    type data = {
        [key: string]: any;
    };

    // Create new class instance, fill it and validate via "class-validator"
    export const validate = <D extends data, C extends {new(): D}>
      (data: D, classTemplate: C): boolean => {
        const instanceClass = new classTemplate();
        Object.keys(data).forEach((key) => {
            instanceClass[key] = data[key];
        });
        return !validateSync(instanceClass).length;
    }

  1. Используйте класс в качестве интерфейса для статической типизации и класс для проверки
    if (validate(cat, Cat)) {
      // OK
    } else {
      // ERROR
    }