Kontrola typu interfejsu za pomocą maszynopisu

To pytanie jest bezpośrednim analogonem do sprawdzania typu klasy za pomocą maszynopisu

Muszę się dowiedzieć w czasie wykonywania, czy zmienna typu any implementuje interfejs. Oto Mój kod:

interface A{
    member:string;
}

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

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

Jeśli wpiszesz ten kod do maszynopisu, ostatnia linia zostanie oznaczona jako błąd, "nazwa A nie istnieje w bieżącym zakresie". Ale to nie jest prawda, nazwa istnieje w obecnym zakresie. Mogę nawet zmienić deklarację zmiennej na var a:A={member:"foobar"}; bez reklamacji od wydawcy. Po przeglądaniu sieci i znalezienie innego pytania NA więc zmieniłem interfejs do klasy, ale potem nie mogę używać literałów obiektów do tworzenia instancji.

Zastanawiałem się, jak typ A może tak zniknąć, ale spojrzenie na wygenerowany javascript wyjaśnia problem:

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

Nie ma reprezentacji a jako interfejsu, dlatego nie ma możliwości sprawdzenia typu runtime.

Rozumiem, że javascript jako język dynamiczny nie ma pojęcia o interfejsach. Czy jest jakiś sposób na sprawdzenie interfejsów?

Autouzupełnianie maszynopisu ujawnia, że typescript oferuje nawet metodę implements. Jak mogę z niego korzystać ?

Author: Community, 2013-01-20

9 answers

Możesz osiągnąć to, co chcesz bez słowa kluczowego instanceof, ponieważ możesz teraz napisać niestandardowy tekst:

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

Wielu członków

Jeśli musisz sprawdzić wielu członków, aby określić, czy obiekt pasuje do twojego typu, możesz zamiast tego dodać dyskryminator. Poniższy przykład jest najbardziej podstawowym i wymaga zarządzania własnymi dyskryminatorami... musisz wniknąć głębiej w wzorce, aby uniknąć powielania dyskryminatorów.

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);
}
 94
Author: user75525,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 14:42:39

W TypeScript 1.6, user-defined type guard wykona zadanie.

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

I tak jak wspomniał Joe Yang: od TypeScript 2.0, możesz nawet skorzystać z tagged Union type.

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

I działa również z switch.

 52
Author: vilicvane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-23 09:43:22

Typescript 2.0 wprowadzenie tagged union

Typescript 2.0 features

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;
    }
}
 22
Author: Joe Yang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-13 09:59:56

A może Strażnicy zdefiniowani przez użytkownika? 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();
}
 17
Author: Caleb Macdonald Black,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-16 00:37:48

Teraz jest to możliwe, właśnie wydałem ulepszoną wersję kompilatora TypeScript, który zapewnia pełne możliwości odbicia. Można tworzyć instancje klas z ich obiektów metadanych, pobierać metadane z konstruktorów klas i sprawdzać interfejs / klasy w czasie wykonywania. Możesz to sprawdzić tutaj

Przykład użycia:

W jednym z plików maszynopisu Utwórz interfejs i klasę, które implementują go w następujący sposób:

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

class MyClass implements MyInterface {
    counter = 0;

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

Teraz wydrukujmy listę zaimplementowane interfejsy.

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

Skompiluj z reflec-ts i uruchom go:

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

Zobacz odbicie.d. ts dla Interface meta-type details.

aktualizacja: Pełny przykład pracy znajdziesz tutaj

 11
Author: pcan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-22 09:34:51

Chciałbym zauważyć, że TypeScript nie zapewnia bezpośredniego mechanizmu dynamicznego testowania, czy obiekt implementuje konkretny interfejs.

Zamiast tego, Kod maszynopisu może używać techniki JavaScript sprawdzania, czy odpowiedni zestaw elementów jest obecny na obiekcie. Na przykład:

var obj : any = new Foo();

if (obj.someInterfaceMethod) {
    ...
}
 4
Author: Daniel Ribeiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-11 19:18:38

TypeGuards

interface MyInterfaced {
    x: number
}

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

if (isMyInterfaced(obj)) {
    (obj as MyInterfaced ).x;
}
 3
Author: Dmitry Matveev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-28 09:54:57

Oto inna opcja: moduł ts-interface-builder zapewnia narzędzie czasu kompilacji, które konwertuje interfejs TypeScript do deskryptora uruchomieniowego, a ts-interface-checker może sprawdzić, czy obiekt go spełnia.

Dla przykładu OP,

interface A {
  member: string;
}

Najpierw uruchamiasz ts-interface-builder, który tworzy nowy zwięzły Plik z deskryptorem, powiedzmy, foo-ti.ts, którego możesz użyć w następujący sposób:

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" 

Można utworzyć jednoliniową funkcję typu strażnika:

function isA(value: any): value is A { return A.test(value); }
 2
Author: DS.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-26 05:26:07

To samo, co powyżej, gdzie zastosowano strażników zdefiniowanych przez użytkownika , ale tym razem z predykatem funkcji strzałki

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);
 1
Author: Dan Dohotaru,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-21 12:07:31