670 Stimmen

Schnittstellentypprüfung mit Typescript

Diese Frage ist das direkte Analogon zu Klassentypprüfung mit TypeScript

Ich muss zur Laufzeit herausfinden, ob eine Variable vom Typ any eine Schnittstelle implementiert. Hier ist mein Code:

interface A{
    member:string;
}

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

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

Wenn Sie diesen Code in den Typescript-Spielplatz eingeben, wird die letzte Zeile als Fehler markiert: "Der Name A existiert nicht im aktuellen Bereich". Aber das ist nicht wahr, der Name existiert im aktuellen Bereich. Ich kann sogar die Variablendeklaration ändern in var a:A={member:"foobar"}; ohne Beanstandungen seitens des Herausgebers. Nachdem ich das Web durchsucht und die andere Frage zu SO gefunden hatte, änderte ich die Schnittstelle in eine Klasse, aber dann kann ich keine Objektliterale verwenden, um Instanzen zu erstellen.

Ich fragte mich, wie der Typ A auf diese Weise verschwinden konnte, aber ein Blick auf das generierte Javascript erklärt das Problem:

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

Es gibt keine Repräsentation von A als Schnittstelle, daher sind keine Laufzeittypprüfungen möglich.

Ich verstehe, dass Javascript als dynamische Sprache kein Konzept von Schnittstellen hat. Gibt es eine Möglichkeit, Typprüfung für Schnittstellen?

Die Autovervollständigung des typescript-Spielplatzes zeigt, dass typescript sogar eine Methode anbietet implements . Wie kann ich es verwenden?

544voto

Fenton Punkte 221749

Sie können erreichen, was Sie wollen, ohne die instanceof Schlüsselwort, da Sie jetzt benutzerdefinierte Type Guards schreiben können:

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

Viele Mitglieder

Wenn Sie viele Mitglieder überprüfen müssen, um festzustellen, ob ein Objekt Ihrem Typ entspricht, können Sie stattdessen einen Diskriminator hinzufügen. Das folgende Beispiel ist das einfachste und erfordert, dass Sie Ihre eigenen Diskriminatoren verwalten... Sie müssen sich mit den Mustern näher befassen, um sicherzustellen, dass Sie doppelte Diskriminatoren vermeiden.

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

162voto

vilicvane Punkte 10357

In TypeScript 1.6, benutzerdefinierter Typschutz wird die Aufgabe erfüllen.

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

Und wie Joe Yang bereits erwähnte: Seit TypeScript 2.0 können Sie sogar die Vorteile von Tagged Union Type nutzen.

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

Und es funktioniert mit switch auch.

77voto

Wie wäre es mit benutzerdefinierten Typensicherungen? 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();
}

62voto

Joe Yang Punkte 1477

Typescript 2.0 führt tagged union ein

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

20voto

Willem van der Veen Punkte 26043

Typografische Wachen in Maschinenschrift:

TS hat Typenschilder zu diesem Zweck. Sie definieren ihn wie folgt:

Ein Ausdruck, der eine Laufzeitprüfung durchführt, die garantiert, dass der Typ in einem bestimmten Bereich garantiert.

Dies bedeutet im Grunde, dass der TS-Compiler den Typ auf einen spezifischeren Typ eingrenzen kann, wenn ihm genügend Informationen vorliegen. Zum Beispiel:

function foo (arg: number | string) {
    if (typeof arg === 'number') {
        // fine, type number has toFixed method
        arg.toFixed()
    } else {
        // Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?
        arg.toFixed()
        // TSC can infer that the type is string because 
        // the possibility of type number is eliminated at the if statement
    }
}

Um auf Ihre Frage zurückzukommen, können wir dieses Konzept der Type Guards auch auf Objekte anwenden, um ihren Typ zu bestimmen. Um einen Type Guard für Objekte zu definieren, müssen wir eine Funktion definieren, deren Rückgabetyp ein Typprädikat . Zum Beispiel:

interface Dog {
    bark: () => void;
}

// The function isDog is a user defined type guard
// the return type: 'pet is Dog' is a type predicate, 
// it determines whether the object is a Dog
function isDog(pet: object): pet is Dog {
  return (pet as Dog).bark !== undefined;
}

const dog: any = {bark: () => {console.log('woof')}};

if (isDog(dog)) {
    // TS now knows that objects within this if statement are always type Dog
    // This is because the type guard isDog narrowed down the type to Dog
    dog.bark();
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X