Verifica se un object implementa un’interfaccia in fase di runtime con TypeScript

Carico un file di configurazione JSON in fase di esecuzione e utilizzo un’interfaccia per definire la struttura prevista:

interface EngineConfig { pathplanner?: PathPlannerConfig; debug?: DebugConfig; ... } interface PathPlannerConfig { nbMaxIter?: number; nbIterPerChunk?: number; heuristic?: string; } interface DebugConfig { logLevel?: number; } ... 

Questo rende conveniente accedere alle varie proprietà poiché posso utilizzare i completamenti automatici ecc.

Domanda: esiste un modo per utilizzare questa dichiarazione per verificare la correttezza del file che carico? cioè che non ho proprietà inaspettate?

No.

Attualmente, i tipi vengono utilizzati solo durante lo sviluppo e il tempo di compilazione. Le informazioni sul tipo non sono tradotte in alcun modo nel codice JavaScript compilato.

Da https://stackoverflow.com/a/16016688/318557 , come sottolineato da @JasonEvans

Sospetto che TypeScript sia (saggiamente) aderente alla legge di Curly, e Typescript è un traspolatore, non un validatore di oggetti. Detto questo, penso anche che le interfacce dattiloscritte farebbero per la convalida degli oggetti scadenti, perché le interfacce hanno un vocabolario (meravigliosamente) limitato e non possono validare le forms che altri programmatori possono usare per distinguere gli oggetti, come la lunghezza dell’array, il numero di proprietà, proprietà del modello, ecc.

Quando si utilizzano oggetti dal codice non typescript, utilizzo un pacchetto di convalida JSONSchema , ad esempio AJV , per la convalida del runtime e un generatore di file .d.ts (come DTSgenerator o DTS-generator ) per compilare definizioni di tipo TypeScript dal mio JSONshcema.

L’avvertenza principale è che, poiché i JSONschemata sono in grado di descrivere forms che non possono essere distinte dal typescript (come patternProperties ), non è una traduzione one-to-one dallo schema JSON a .t.ds, e potrebbe essere necessario fare qualche mano modifica di file .d.ts generati quando si utilizzano tali schemi JSON.

Detto questo, poiché altri programmatori possono usare proprietà come la lunghezza dell’array per inferire il tipo di object, ho l’abitudine di distinguere i tipi che potrebbero essere confusi dal compilatore TypeScript usando enum per impedire al transpiler di accettare l’uso di un tipo al posto del altro, così:

 [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 

Che genera un file .d.ts modo:

 [MyTypes.d.ts] interface typeA{ type: "A"; foo: string[]; } interface typeB{ type: "B"; foo: string[]; } 

Sì. È ansible eseguire questo controllo in fase di esecuzione utilizzando una versione avanzata del compilatore TypeScript che ho rilasciato qualche tempo fa. Puoi fare qualcosa di simile al seguente:

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

e questo è l’output:

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

Si noti che la funzione isValid funziona in modo ricorsivo , quindi è ansible utilizzarlo anche per convalidare oggetti nidificati. Qui puoi trovare l’esempio completo di lavoro

Lì “è” un modo, ma devi implementarlo tu stesso. Si chiama “Guard di tipo definito dall’utente” e assomiglia a questo:

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

Naturalmente, l’effettiva implementazione della funzione isTest da te, ma la parte buona è che si tratta di una funzione reale, il che significa che è testabile.

Ora in fase di esecuzione si utilizza isTest() per verificare se un object rispetta un’interfaccia. Al momento della compilazione il typescript prende in mano la guardia e tratta il successivo utilizzo come previsto, cioè:

 let a:any = { prop: 5 }; ax; //ok because here a is of type any if (isTest(a)) { ax; //error because here a is of type Test } 

Spiegazioni più approfondite qui: https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html

Ecco un’altra alternativa, specificamente per questo:

ts-interface-builder è uno strumento che si esegue in fase di compilazione sul proprio file TypeScript (ad es. foo.ts ) per creare descrittori di runtime (ad es. foo-ti.ts ).

ts-interface-checker usa questi per validare oggetti in fase di runtime. Per esempio

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

È ansible utilizzare il metodo strictCheck() per garantire che non vi siano proprietà sconosciute.

Ecco un buon modo. È ansible convertire un’interfaccia TypeScript in uno schema JSON utilizzando typescript-json-schema , ad es

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

Quindi convalidare i dati in fase di esecuzione utilizzando un validatore schema JSON come ajv , ad es

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

Non so come sia il tuo file di configurazione, ma il più ovvio sarebbe il file json, anche se andrei con lo schema json per convalidare se il file si adatta allo schema o meno.

Ecco la documentazione di json schema v4: http://json-schema.org/documentation.html

E uno degli esempi su come puoi testarlo: https://github.com/fge/json-schema-validator

Ovviamente devi scrivere il tuo schema in base alle interfacce, ma non puoi usarle direttamente.

sì, c’è una lib che lo fa https://github.com/gcanti/io-ts

l’idea è semplice, avere semplici controlli per le proprietà composte in controlli più complessi per gli oggetti