Javascript 使用 TypeScript 检查对象是否在运行时实现了接口

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33800497/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 15:30:01  来源:igfitidea点击:

Check if an object implements an interface at runtime with TypeScript

javascripttypescript

提问by MasterScrat

I load a JSON configuration file at runtime, and use an interface to define its expected structure:

我在运行时加载一个 JSON 配置文件,并使用一个接口来定义其预期结构:

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

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

interface DebugConfig {
    logLevel?: number;
}

...

This makes it convenient to access the various properties since I can use autocompletions etc.

这使得访问各种属性变得方便,因为我可以使用自动完成等。

Question: is there a way to use this declaration to check the correctness of the file I load? ie that I do not have unexpected properties?

问题:有没有办法使用这个声明来检查我加载的文件的正确性?即我没有意外的属性?

采纳答案by MasterScrat

No.

Currently, types are used only during development and compile time. The type information is not translated in any way to the compiled JavaScript code.

不。

目前,类型仅在开发和编译期间使用。类型信息不会以任何方式转换为已编译的 JavaScript 代码。

From https://stackoverflow.com/a/16016688/318557, as pointed out by @JasonEvans

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

There is an open issue since Jun 2015 about this in the TypeScript repo: https://github.com/microsoft/TypeScript/issues/3628

自 2015 年 6 月以来,TypeScript 存储库中有一个关于此的未解决问题:https: //github.com/microsoft/TypeScript/issues/3628

回答by Teodor Sandu

There "is" a way, but you have to implement it yourself. It's called a "User Defined Type Guard" and it looks like this:

“有”一种方法,但您必须自己实施。它被称为“用户定义的类型保护”,它看起来像这样:

interface Test {
    prop: number;
}

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

Of course, the actual implementation of the isTestfunction is totally up to you, but the good part is that it's an actual function, which means it's testable.

当然,isTest函数的实际实现完全取决于你,但好的部分是它是一个实际的函数,这意味着它是可测试的。

Now at runtime you would use isTest()to validate if an object respects an interface. At compile time typescript picks up on the guard and treats subsequent usage as expected, i.e.:

现在在运行时,您将使用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
}

More in-depth explanations here: https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html

更深入的解释在这里:https: //basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html

回答by DS.

Here is another alternative, specifically for this:

这是另一种选择,专门用于此:

ts-interface-builderis a tool you run at build time on your TypeScript file (e.g. foo.ts) to build runtime descriptors (e.g. foo-ti.ts).

ts-interface-builder是您在构建时在 TypeScript 文件(例如foo.ts)上运行以构建运行时描述符(例如foo-ti.ts)的工具。

ts-interface-checkeruses these to validate objects at runtime. E.g.

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

You can use strictCheck()method to ensure there are no unknown properties.

您可以使用strictCheck()方法来确保没有未知的属性。

回答by DS.

Here's a good way. You can convert a TypeScript interface to JSON schema using typescript-json-schema, e.g.

这里有一个好方法。您可以使用typescript-json-schema将 TypeScript 接口转换为 JSON模式,例如

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

Then validate data at runtime using a JSON schema validator such as ajv, e.g.

然后在运行时使用 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);
}

回答by Jthorpe

I suspect that TypeScript is (wisely) adhering to Curly's Law, and Typescript is a transpiler, not an object validator. That said, I also think that typescript interfaces would make for lousy object validation, because interfaces have a (wonderfully) limited vocabulary and can't validate against shapes that other programmers may use to distinguish objects, such as array length, number of properties, pattern properties, etc.

我怀疑 TypeScript 是(明智地)遵守 Curly 定律,而 Typescript 是一个转译器,而不是一个对象验证器。也就是说,我还认为 typescript 接口会导致糟糕的对象验证,因为接口的词汇量(非常)有限,并且无法针对其他程序员可能用来区分对象的形状进行验证,例如数组长度、属性数量、模式属性等。

When consuming objects from non-typescript code, I use a JSONSchemavalidation package, such as AJV, for run-time validation, and a .d.ts file generator (such as DTSgeneratoror DTS-generator) to compile TypeScript type definitions from my JSONshcema.

当消耗来自非打字稿代码对象,我使用了JSONSchema验证包,如AJV,用于运行时间验证,和一个.d.ts文件发生器(如DTSgeneratorDTSgenerator)从编译打字原稿类型定义我JSONshcema。

The major caveat is that because JSONschemata are capable of describing shapes that cannot be distinguished by typescript (such as patternProperties), it's not a one-to-one translation from JSON schema to .t.ds, and you may have to do some hand editing of generated .d.ts files when using such JSON schemata.

主要的警告是,因为 JSONschemata 能够描述无法通过打字稿区分的形状(例如patternProperties),它不是从 JSON 模式到 .t.ds 的一对一转换,您可能需要做一些手工使用此类 JSON 模式时编辑生成的 .d.ts 文件。

That said, because other programmers may use properties like array length to infer object type, I'm in the habit of distinguishing types that could be confused by the TypeScript compiler using enum's to prevent the transpiler from accepting use of one type in place of the other, like so:

也就是说,因为其他程序员可能使用数组长度之类的属性来推断对象类型,所以我习惯于区分可能被 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

Which generates a .d.tsfile like so:

它会生成一个.d.ts像这样的文件:

[MyTypes.d.ts]

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

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

回答by pcan

Yes.You can do this check at runtime by using an enhanced version of the TypeScript compiler that I released a few time ago. You can do something like the following:

是的。您可以使用我前几天发布的增强版 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");

and this is the output:

这是输出:

isValid(personOk):  true

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

Please note that the isValidfunction works recursively, so you can use it to validate nested objects, too. You can find the full working example here

请注意,该isValid函数以递归方式工作,因此您也可以使用它来验证嵌套对象。您可以在此处找到完整的工作示例

回答by Trident D'Gao

yes, there is a lib that does it https://github.com/gcanti/io-ts

是的,有一个库可以做到这一点https://github.com/gcanti/io-ts

the idea is simple, have simple checks for properties composed into more complex checks for objects

这个想法很简单,将简单的属性检查组合成更复杂的对象检查

回答by Alexey Baranoshnikov

You can use class-validation

您可以使用类验证

  1. Replace interface to class.
  1. 将接口替换为类。
    class Cat {
        @IsNotEmpty() name: string;
    }

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

  1. Create validation function. Example:
  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. Use class as interface for static typing and class for validation
  1. 使用类作为静态类型的接口和类进行验证
    if (validate(cat, Cat)) {
      // OK
    } else {
      // ERROR
    }

回答by Moshe Gottlieb

I realize this question is old, but I just wrote my own validator for JSON objects and typescript, for this exact purpose, using decorators.
Available here: ts-json-object.
Typescript has moved on a bit since this question was asked, and now has experimental features allowing recording of type information for later usage.
The following example validates @requiredand @optionalproperties, but also validates their type, even though there is no mentioning of the type in the validation notation.

我意识到这个问题很老,但我只是为 JSON 对象和打字稿编写了自己的验证器,为此目的,使用装饰器。
可在此处获得:ts-json-object
自从提出这个问题以来,Typescript 已经有了一些进展,现在具有允许记录类型信息以供以后使用的实验性功能。
下面的示例验证@required@optional属性,但也验证它们的类型,即使在验证符号中没有提到类型。

Example:

例子:

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

Has many other features such as custom validations, etc.

具有许多其他功能,例如自定义验证等。

回答by Maciej Kwas

I don't know how your configuration file looks like, but most obvious would be json file, though I would go with json schema to validate if file fits the schema or not.

我不知道你的配置文件是什么样的,但最明显的是 json 文件,尽管我会使用 json 模式来验证文件是否符合模式。

Here's json schema v4 documentation: http://json-schema.org/documentation.html

这是 json 模式 v4 文档:http: //json-schema.org/documentation.html

And one of examples how you could test it: https://github.com/fge/json-schema-validator

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

Of course you have to write your schema based on interfaces, but you can't use them directly.

当然,您必须根据接口编写架构,但不能直接使用它们。