2017-08-07 26 views
0

Ich habe ein Problem, das diese Fehler auf der Konsole erzeugt.Typoskript - Generika? Oder etwas anderes?

Type 'WebFieldB' does not satisfy the constraint 'WebFieldA & WebFieldB'. 

Es gibt 2 vom Server generierte Klassen, die nicht geändert werden sollten.

class WebFieldA { 
    readonly Name: string; 
    readonly IsUpdatable: boolean; 
} 

class WebFieldB { 
    readonly Name: string; 
} 

Und dies ist mein Code:

interface IFieldInfo { 
    name: string; 
    isUpdatable: boolean; 
} 

class SomeField<T extends WebFieldA & WebFieldB> implements IFieldInfo { 
    field: T; 

    constructor(pField: T) { 
     this.field = pField; 
    } 

    get name(): string { 
     return this.field.Name; 
    }; 

    get isUpdatable(): boolean { 
     return this.field.IsUpdatable || false; 
    } 
} 

let field1: IFieldInfo = new SomeField<WebFieldA>({Name: 'fieldA', IsUpdatable: false}); 
let field2: IFieldInfo = new SomeField<WebFieldB>({Name: 'fieldB'}); **// Here for WebFieldB is error** 
let myCollection: Array<IFieldInfo> = [field1, field2]; 

Was erwarte ich? Ich hätte gerne eine Sammlung von IFieldInfo aus Objekten von 2 Arten gebaut: WebFieldA und WebFieldB (WFB hat keine isUpdatable Eigentum!). Leider kann ich nur meinen Code ändern, nicht WebFieldA oder WebFieldB.

Und vielleicht eine andere Idee, dies ohne Generika zu machen? Ich weiß nicht ...

Antwort

1

Ich bin nicht genau sicher, ob dies Ihr Endziel erreichen wird, aber Sie können Ihren Code kompilieren, wenn Sie es so ändern.

class SomeField<T extends (WebFieldA | WebFieldB)> implements IFieldInfo { 
    // .... 
    get isUpdatable(): boolean { 
     return this.field instanceof WebFieldA && this.field.IsUpdatable; 
    } 
} 

Ich habe ein paar Änderungen vorgenommen. Zuerst habe ich | anstelle von & verwendet, um anzuzeigen, dass T nur eine der Klassen erweitern muss, und nicht beide. Dann fügte ich dem isUpdatable Property Getter einen Typwächter hinzu, um sicherzustellen, dass die Eigenschaft nur gelesen wird, wenn T ein WebFieldA oder eine Unterklasse davon ist.

+0

Sie sollten wahrscheinlich den Typwächter in etwas ändern, das 'this.field.IsUpdatable' direkt (strukturell) testet, da der Beispielcode gegen Objekte testet, die nicht 'instanceof' der Klassen 'WebFieldA' oder 'WebFieldB' sind. – jcalz

+0

Es hat funktioniert. Danke vielmals! :-) – str1ct

+1

@ str1ct, sei vorsichtig, teste gegen '(new SomeField ({Name: 'fieldA', IsUpdatable: true})). IsUpdatable' zur Laufzeit – jcalz

2

Wie @recursive schlägt vor, Ihr Eingabefeld ein WebFieldAoder ein WebFieldB, kein WebFieldAund ein WebFieldB sein wird. Das bedeutet, dass Sie WebFieldA | WebFieldB statt WebFieldA & WebFieldB verwenden sollten.

Die Implementierung von isUpdatable ist dann ein Problem, weil es annimmt, dass es ein IsUpdatable Feld auf this.field gibt. Sie können dies mit einem geeigneten Typenschutz lösen. Verwenden Sie instanceof nicht als Antwort von @ recursive, es sei denn, Sie planen nur, die tatsächlichen Instanzen WebFieldA und WebFieldB im Gegensatz zu Objekten mit denselben Eigenschaften zu übergeben. Ich sehe, dass Ihr Beispielcode einfache Objekte verwendet. Verwenden Sie daher nicht instanceof.

Eine Art Wache ich verwenden möchte, ist die folgende nützliche Funktion (es wahrscheinlich in einer Bibliothek sein sollte):

function hasKey<K extends string>(key: K, obj: any): obj is {[P in K]: any} { 
    return key in obj; 
} 

Dies ist nur dafür sorgt, dass ein Objekt einen bestimmten Schlüssel hat. Dann können Sie isUpdatable ohne Fehler wie folgt implementieren:

get isUpdatable(): boolean { 
    return hasKey('IsUpdatable',this.field) ? this.field.IsUpdatable : false; 
} 

Das ist ein bisschen viel, aber. Lassen Sie uns das Type-Guard-Problem umgehen. Beachten Sie, dass strukturell gesehen, WebFieldA | WebFieldB ist sehr ähnlich zu einer einzigen Schnittstelle wie

interface SomeWebField { 
    readonly Name: string; 
    readonly IsUpdatable?: boolean; // optional 
} 

und beachten Sie, dass Sie nicht überhaupt Gebrauch von Generika zu machen.Genauso gut könnte man ersetzen Sie einfach den Gattungs mit SomeWebField:

class SomeField implements IFieldInfo { 
    field: SomeWebField; 

    constructor(pField: SomeWebField) { 
     this.field = pField; 
    } 

    get name(): string { 
     return this.field.Name; 
    }; 

    get isUpdatable(): boolean { 
     return this.field.IsUpdatable || false; 
    } 
} 

let field1: IFieldInfo = new SomeField({Name: 'fieldA', IsUpdatable: false}); 
let field2: IFieldInfo = new SomeField({Name: 'fieldB'}); 
let myCollection: Array<IFieldInfo> = [field1, field2]; 

Vorausgesetzt, dass Sie nicht andere versteckte Anwendungsfälle haben haben Sie nicht erwähnt, die über Ihre Bedürfnisse ohne Generika erfüllen sollte.

Hoffnung, die hilft; Viel Glück!

+0

Natürlich. Das hilft. Ich wollte wissen, ob es einen anderen Weg gibt, das zu tun. Und es ist so einfach ... Ich denke, dieses Thema ist erschöpft. Vielen Dank an alle :) – str1ct