javascript - Verificați dacă un obiect implementează o interfață în timpul rulării cu TypeScript


Încărc un fișier de configurare JSON la rulare și folosesc o interfață pentru a-i defini structura așteptată:

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

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

interface DebugConfig {
    logLevel?: number;
}

...

Acest lucru face convenabil accesul la diversele proprietăți deoarece pot folosi completările automate etc.

Întrebare: există o modalitate de a folosi această declarație pentru a verifica corectitudinea fișierului pe care îl încarc? adică să nu am proprietăți neașteptate?

Răspuns #1

Există o cale, dar trebuie să o implementezi singur. Se numește „User Defined Type Guard” și arată așa:

interface Test {
    prop: number;
}

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

Desigur, implementarea efectivă a funcției isTest depinde complet de tine, dar partea bună este că este o funcție reală, ceea ce înseamnă că poate fi testată.

Acum, în timpul rulării, ai folosi isTest() pentru a valida dacă un obiect respectă o interfață. La compilare, TypeScript recunoaște protecția și tratează utilizarea ulterioară conform așteptărilor, adică:

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
}

Explicații mai detaliate aici: https://basarat.gitbook.io/typescript/type-system/typeguard

Răspunsul #2

Iată o altă alternativă, special pentru asta:

ts-interface-builder este un instrument pe care îl rulezi la timpul de construire pe fișierul tău TypeScript (de exemplu foo.ts) pentru a construi descrieri la runtime (de exemplu foo-ti.ts).

ts-interface-checker folosește acestea pentru a valida obiectele în timpul rulării. De exemplu.

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);

Puteți folosi metoda strictCheck() pentru a vă asigura că nu există proprietăți necunoscute.

Răspunsul #3

Nu.

În prezent, tipurile sunt folosite doar în timpul dezvoltării și al compilării. Informațiile despre tipuri nu sunt traduse în niciun fel în codul JavaScript compilat.

De pe https://stackoverflow.com/a/16016688/318557, așa cum a subliniat @JasonEvans

Există o problemă deschisă din iunie 2015 despre aceasta în repo-ul TypeScript: https://github.com/microsoft/TypeScript/issues/3628

Răspunsul #4

Iată o modalitate bună. Poți converti o interfață TypeScript într-un schema JSON folosind typescript-json-schema, de exemplu.

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

Apoi validează datele la rulare folosind un validator de schemă JSON, cum ar fi ajv, de exemplu.

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);
}