簡體   English   中英

檢查對象是否在運行時使用 TypeScript 實現接口

[英]Check if an object implements an interface at runtime with TypeScript

我在運行時加載一個 JSON 配置文件,並使用一個接口來定義其預期結構:

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

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

interface DebugConfig {
    logLevel?: number;
}

...

這使得訪問各種屬性變得方便,因為我可以使用自動完成等。

問題:有沒有辦法使用這個聲明來檢查我加載的文件的正確性? 即我沒有意外的屬性?

“有”一種方法,但您必須自己實施。 它被稱為“用戶定義的類型保護”,它看起來像這樣:

interface Test {
    prop: number;
}

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

當然, 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.gitbook.io/typescript/type-system/typeguard

不。

目前,類型僅在開發和編譯期間使用。 類型信息不會以任何方式轉換為已編譯的 JavaScript 代碼。

正如@JasonEvans 所指出的,來自https://stackoverflow.com/a/16016688/318557

自 2015 年 6 月以來,TypeScript 存儲庫中有一個關於此的未解決問題: https : //github.com/microsoft/TypeScript/issues/3628

這是另一種選擇,專門用於此:

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()方法來確保沒有未知屬性。

這里有一個好方法。 您可以使用typescript-json-schema將 TypeScript 接口轉換為 JSON模式,例如

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

我懷疑 TypeScript 是(明智地)遵守 Curly 定律,而 Typescript 是一個轉譯器,而不是一個對象驗證器。 也就是說,我還認為 typescript 接口會導致糟糕的對象驗證,因為接口的詞匯量(非常)有限,並且無法針對其他程序員可能用來區分對象的形狀進行驗證,例如數組長度、屬性數量、模式屬性等。

當消耗來自非打字稿代碼對象,我使用了JSONSchema驗證包,如AJV ,用於運行時間驗證,和一個.d.ts文件發生器(如DTSgeneratorDTSgenerator )從編譯打字原稿類型定義我JSONshcema。

主要的警告是,因為 JSONschemata 能夠描述無法通過打字稿(例如patternProperties )區分的形狀,它不是從 JSON 模式到 .t.ds 的一對一轉換,您可能需要做一些手工使用此類 JSON 模式時編輯生成的 .d.ts 文件。

也就是說,因為其他程序員可能使用數組長度之類的屬性來推斷對象類型,所以我習慣於區分可能被 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[];
}

是的。 您可以使用我前幾天發布的增強版 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函數遞歸地工作,因此您也可以使用它來驗證嵌套對象。 您可以在此處找到完整的工作示例

是的,有一個庫可以做到這一點https://github.com/gcanti/io-ts

這個想法很簡單,將簡單的屬性檢查組合成更復雜的對象檢查

我意識到這個問題很老,但我只是為 JSON 對象和打字稿編寫了自己的驗證器,為此目的,使用裝飾器。
可在此處獲得: ts-json-object
自從提出這個問題以來,Typescript 已經有了一些進展,現在具有允許記錄類型信息以供以后使用的實驗性功能。
下面的示例驗證@required@optional屬性,但也驗證它們的類型,即使在驗證符號中沒有提到類型。

例子:

import {JSONObject,required,optional,lt,gte} from 'ts-json-object'

class Person extends JSONObject {
    @required // required
    name: string
    @optional // optional!
    @lt(150) // less than 150
    @gte(0) // Greater or equal to 0
    age?: number
}

let person = new Person({
 name: 'Joe'
}) // Ok
let person = new Person({
}) // Will throw a TypeError, because name is required
let person = new Person({
 name: 123
}) // Will throw a TypeError, because name must be a string

具有許多其他功能,例如自定義驗證等。

我不知道你的配置文件是什么樣的,但最明顯的是 json 文件,盡管我會使用 json 模式來驗證文件是否符合模式。

這是 json 模式 v4 文檔: http : //json-schema.org/documentation.html

以及如何測試它的示例之一: https : //github.com/fge/json-schema-validator

當然,您必須根據接口編寫架構,但不能直接使用它們。

您可以使用類驗證

  1. 用類替換接口。
class Cat {
        @IsNotEmpty() name: string;
    }
    
    // Static typing works!
    const cat: Cat = { 
        name: "Barsik"
    };
  1. 創建驗證函數。 例子:
import { validateSync } from "class-validator";
    
    type data = {
        [key: string]: any;
    };
    
    // Create new class instance 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
    }

剛剛制作了一個簡單的站點,用於從打字稿接口生成 JavaScript 驗證代碼。 注意:請仔細閱讀限制。

https://ts-interface-validator.vercel.app/

為了堆積“使用這個庫”的答案,這是我的:我創建了一個名為ts-data-checker的包,它在運行時運行 TypeScript 語言服務來檢查 JSON:

import { checker } from "ts-data-checker";

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

const { checkJson } = checker("PathPlannerConfig", "./nameofthisfile");

if (checkJson(`{ "nbMaxIter": 1 }`)) {
    console.log('valid!');
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM