Проверка типа интерфейса с помощью Typescript



этот вопрос является прямым аналогом проверка типа класса с помощью TypeScript



мне нужно выяснить во время выполнения, если переменная типа any реализует интерфейс. Вот мой код:



interface A{
member:string;
}

var a:any={member:"foobar"};

if(a instanceof A) alert(a.member);


если вы введете этот код на игровой площадке typescript, последняя строка будет отмечена как ошибка: "имя A не существует в текущей области". Но это не так, имя существует в текущей области. Я даже могу изменить объявление переменной на var a:A={member:"foobar"}; без жалоб от редактора. После просмотра веб-страниц и поиска другого вопроса, поэтому я изменил интерфейс на класс, но тогда я не могу использовать объектные литералы для создания экземпляров.



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



var a = {
member: "foobar"
};
if(a instanceof A) {
alert(a.member);
}


нет представления a как интерфейса, поэтому никакие проверки типа времени выполнения не возможны.



Я понимаю, что JavaScript как динамический язык не имеет понятия интерфейсов. Есть ли способ ввести проверку интерфейсов?



автозаполнение typescript playground показывает, что typescript даже предлагает метод implements. Как я могу его использовать ?

1410   9  

9 ответов:

вы можете достичь того, чего вы хотите, без instanceof ключевое слово, как вы можете написать пользовательский тип охранников сейчас:

interface A{
    member:string;
}

function instanceOfA(object: any): object is A {
    return 'member' in object;
}

var a:any={member:"foobar"};

if (instanceOfA(a)) {
    alert(a.member);
}

много членов

Если вам нужно проверить много членов, чтобы определить, соответствует ли объект вашему типу, вы можете вместо этого добавить дискриминатор. Ниже приведен самый простой пример, и требует, чтобы вы управляли своими собственными дискриминаторами... вам нужно будет углубиться в шаблоны, чтобы избежать дублирования дискриминаторов.

interface A{
    discriminator: 'I-AM-A';
    member:string;
}

function instanceOfA(object: any): object is A {
    return object.discriminator === 'I-AM-A';
}

var a:any = {discriminator: 'I-AM-A', member:"foobar"};

if (instanceOfA(a)) {
    alert(a.member);
}

В TypeScript 1.6, определяемый пользователем тип guard сделает работу.

interface Foo {
    fooProperty: string;
}

interface Bar {
    barProperty: string;
}

function isFoo(object: any): object is Foo {
    return 'fooProperty' in object;
}

let object: Foo | Bar;

if (isFoo(object)) {
    // `object` has type `Foo`.
    object.fooProperty;
} else {
    // `object` has type `Bar`.
    object.barProperty;
}

и так же, как упоминал Джо Янг: начиная с TypeScript 2.0, вы даже можете воспользоваться преимуществами типа tagged union.

interface Foo {
    type: 'foo';
    fooProperty: string;
}

interface Bar {
    type: 'bar';
    barProperty: number;
}

let object: Foo | Bar;

// You will see errors if `strictNullChecks` is enabled.
if (object.type === 'foo') {
    // object has type `Foo`.
    object.fooProperty;
} else {
    // object has type `Bar`.
    object.barProperty;
}

и switch тоже.

typescript 2.0 вводит тег union

Typescript 2.0 особенности

interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {
    // In the following switch statement, the type of s is narrowed in each case clause
    // according to the value of the discriminant property, thus allowing the other properties
    // of that variant to be accessed without a type assertion.
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return Math.PI * s.radius * s.radius;
    }
}

Как насчет пользовательских типов охранников? https://www.typescriptlang.org/docs/handbook/advanced-types.html

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function isFish(pet: Fish | Bird): pet is Fish { //magic happens here
    return (<Fish>pet).swim !== undefined;
}

// Both calls to 'swim' and 'fly' are now okay.

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}

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

пример использования:

в одном из ваших файлов typescript создайте интерфейс и класс, который реализует его как следующее:

interface MyInterface {
    doSomething(what: string): number;
}

class MyClass implements MyInterface {
    counter = 0;

    doSomething(what: string): number {
        console.log('Doing ' + what);
        return this.counter++;
    }
}

теперь давайте напечатаем список реализованных интерфейсов.

for (let classInterface of MyClass.getClass().implements) {
    console.log('Implemented interface: ' + classInterface.name)
}

скомпилировать с reflec-ts и запустить его:

$ node main.js
Implemented interface: MyInterface
Member name: counter - member kind: number
Member name: doSomething - member kind: function

увидеть свое отражение.d. ts для Interface мета-тип детали.

обновление: Вы можете найти полный рабочий пример здесь

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

вместо этого код TypeScript может использовать метод JavaScript для проверки наличия соответствующего набора элементов в объекте. Например:

var obj : any = new Foo();

if (obj.someInterfaceMethod) {
    ...
}

TypeGuards

interface MyInterfaced {
    x: number
}

function isMyInterfaced(arg: any): arg is MyInterfaced {
    return arg.x !== undefined;
}

if (isMyInterfaced(obj)) {
    (obj as MyInterfaced ).x;
}

вот еще один вариант: модуль TS-interface-builder предоставляет инструмент времени сборки, который преобразует интерфейс TypeScript в дескриптор среды выполнения, и TS-interface-checker можете проверить, если объект удовлетворяет его.

для примера OP,

interface A {
  member: string;
}

ты бы сначала запустить ts-interface-builder который создает новый сжатый файл с дескриптором, скажем,foo-ti.ts, который вы можете использовать такой:

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

A.check({member: "hello"});           // OK
A.check({member: 17});                // Fails with ".member is not a string" 

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

function isA(value: any): value is A { return A.test(value); }

то же, что и выше, где пользовательские охранников были использованы, но на этот раз с предикатом функции стрелки

interface A {
  member:string;
}

const check = (p: any): p is A => p.hasOwnProperty('member');

var foo: any = { member: "foobar" };
if (check(foo))
    alert(foo.member);

Comments

    Ничего не найдено.