391 Stimmen

TypeScript und Feldinitialisierungen

Wie initiiert man eine neue Klasse in TS auf diese Weise (Beispiel in C#, um zu zeigen, was ich will):

// ... some code before
return new MyClass { Field1 = "ASD", Field2 = "QWE" };
// ...  some code after

15voto

Jacob Foshee Punkte 2543

In einigen Fällen kann es akzeptabel sein, Folgendes zu verwenden Object.create . Die Mozilla-Referenz enthält ein Polyfill, wenn Sie Rückwärtskompatibilität benötigen oder eine eigene Initialisierungsfunktion erstellen möchten.

Angewandt auf Ihr Beispiel:

Object.create(Person.prototype, {
    'Field1': { value: 'ASD' },
    'Field2': { value: 'QWE' }
});

Nützliche Szenarien

  • Einheitliche Tests
  • Inline-Erklärung

In meinem Fall fand ich dies in Unit-Tests aus zwei Gründen nützlich:

  1. Wenn ich Erwartungen teste, möchte ich oft ein schlankes Objekt als Erwartung erstellen
  2. Unit-Test-Frameworks (wie Jasmine) können den Objektprototyp ( __proto__ ) und den Test nicht bestehen. Zum Beispiel:

    var actual = new MyClass(); actual.field1 = "ASD"; expect({ field1: "ASD" }).toEqual(actual); // fails

Die Ausgabe des fehlgeschlagenen Einheitstests gibt keinen Aufschluss darüber, was nicht übereinstimmt.

  1. In Unit-Tests kann ich auswählen, welche Browser ich unterstütze

Schließlich wird die unter http://typescript.codeplex.com/workitem/334 unterstützt keine Inline-Deklaration im Json-Stil. Das folgende Beispiel lässt sich nicht kompilieren:

var o = { 
  m: MyClass: { Field1:"ASD" }
};

14voto

VitalyB Punkte 11591

Ich wollte eine Lösung, die Folgendes bietet:

  • Alle Datenobjekte sind erforderlich und müssen vom Konstruktor gefüllt werden.
  • Es müssen keine Standardwerte angegeben werden.
  • Kann Funktionen innerhalb der Klasse verwenden.

Ich mache es folgendermaßen:

export class Person {
  id!: number;
  firstName!: string;
  lastName!: string;

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  constructor(data: OnlyData<Person>) {
    Object.assign(this, data);
  }
}

const person = new Person({ id: 5, firstName: "John", lastName: "Doe" });
person.getFullName();

Alle Eigenschaften im Konstruktor sind obligatorisch und dürfen nicht weggelassen werden, ohne dass ein Compilerfehler auftritt.

Sie ist abhängig von der OnlyData die herausfiltert getFullName() aus den erforderlichen Eigenschaften und ist wie folgt definiert:

// based on : https://medium.com/dailyjs/typescript-create-a-condition-based-subset-types-9d902cea5b8c
type FilterFlags<Base, Condition> = { [Key in keyof Base]: Base[Key] extends Condition ? never : Key };
type AllowedNames<Base, Condition> = FilterFlags<Base, Condition>[keyof Base];
type SubType<Base, Condition> = Pick<Base, AllowedNames<Base, Condition>>;
type OnlyData<T> = SubType<T, (_: any) => any>;

Die derzeitigen Grenzen dieser Methode:

  • Erfordert TypeScript 2.8
  • Klassen mit Gettern/Settern

13voto

Ralph Lavelle Punkte 5500

Ich würde eher dazu tendieren, es auf diese Weise zu machen, mit (optionalen) automatischen Eigenschaften und Standardwerten. Sie haben nicht vorgeschlagen, dass die beiden Felder Teil einer Datenstruktur sind, deshalb habe ich diesen Weg gewählt.

Sie könnten die Eigenschaften der Klasse haben und sie dann auf die übliche Weise zuweisen. Und natürlich können sie erforderlich sein oder auch nicht, also ist das auch etwas anderes. Es ist nur so, dass dies ein schöner syntaktischer Zucker ist.

class MyClass{
    constructor(public Field1:string = "", public Field2:string = "")
    {
        // other constructor stuff
    }
}

var myClass = new MyClass("ASD", "QWE");
alert(myClass.Field1); // voila! statement completion on these properties

13voto

AlexPavlov Punkte 267

Sie könnten eine Klasse mit optionalen Feldern (markiert mit ?) und einen Konstruktor haben, der eine Instanz derselben Klasse erhält.

class Person {
    name: string;     // required
    address?: string; // optional
    age?: number;     // optional

    constructor(person: Person) {
        Object.assign(this, person);
    }
}

let persons = [
    new Person({ name: "John" }),
    new Person({ address: "Earth" }),    
    new Person({ age: 20, address: "Earth", name: "John" }),
];

In diesem Fall können Sie die Pflichtfelder nicht auslassen. Auf diese Weise können Sie die Konstruktion des Objekts sehr genau steuern.

Sie könnten den Konstruktor mit dem Typ Partial verwenden, wie in anderen Antworten erwähnt:

public constructor(init?:Partial<Person>) {
    Object.assign(this, init);
}

Das Problem ist, dass alle Felder optional werden, was in den meisten Fällen nicht wünschenswert ist.

5voto

bertvanbrakel Punkte 59

Um eine Klasse zu initiieren, ohne alle Eigenschaften für Standardwerte neu zu deklarieren:

class MyClass{ 
  prop1!: string  //required to be passed in
  prop2!: string  //required to be passed in
  prop3 = 'some default'
  prop4 = 123

  constructor(opts:{prop1:string, prop2:string} & Partial<MyClass>){
    Object.assign(this,opts)
  }
}

Dies kombiniert einige der bereits ausgezeichneten Antworten

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