Typdefinition im Objektliteral in TypeScript


343

In TypeScript-Klassen können Typen für Eigenschaften deklariert werden, zum Beispiel:

class className {
  property: string;
};

Wie deklariert man den Typ einer Eigenschaft in einem Objektliteral?

Ich habe den folgenden Code ausprobiert, aber er wird nicht kompiliert:

var obj = {
  property: string;
};

Ich erhalte die folgende Fehlermeldung:

Der Name 'Zeichenfolge' ist im aktuellen Bereich nicht vorhanden

Mache ich etwas falsch oder ist das ein Fehler?

Antworten:


423

Du bist ziemlich nah dran, du musst nur das =durch ein ersetzen :. Sie können ein Objekttypliteral (siehe Spezifikationsabschnitt 3.5.3) oder eine Schnittstelle verwenden. Die Verwendung eines Objekttyp-Literals entspricht in etwa dem, was Sie haben:

var obj: { property: string; } = { property: "foo" };

Sie können aber auch eine Schnittstelle verwenden

interface MyObjLayout {
    property: string;
}

var obj: MyObjLayout = { property: "foo" };

14
Die DRY-Option (Don't Repeat Yourself) von @Rick Love mit dem Cast-Operator scheint viel ordentlicher zu sein. Nur kommentieren, nicht abstimmen ...
Spechter

Ich weiß, dass dies vor einiger Zeit beantwortet wurde, aber irre ich mich in den Annahmen, dass Probleme auftreten können, wenn die Klasse sowohl Methoden als auch Eigenschaften hat? Da die Klasse nicht initialisiert wird und wir nur Eigenschaften zuweisen, führt der Aufruf einer Methode für die Klasse zu einer Nullausnahme. Grundsätzlich fungiert das Objekt, das wir erstellen, nur als Klasse, da wir seinen Typ zuweisen, aber es ist eigentlich keine Instanz dieser Klasse. Dh wir müssen die Klasse mit dem Schlüsselwort 'new' erstellen. Und dann kehren wir zu Quadrat 1 zurück, da wir so etwas wie new class () nicht machen können {prop: 1}; in TS wie wir es zum Beispiel in C # können.
DeuxAlpha

@DeuxAlpha Dies wird einer Schnittstelle zugewiesen , die keine Methoden haben kann. Ich glaube nicht, dass Sie einer solchen Klasse zuordnen können.
Mog0

Link zur Spezifikation wäre schön :)
ahong

@DeuxAlpha Dies erstellt ein Objektliteral, kein Klassenobjekt. Auch eine Schnittstelle kann Methoden haben. Wenn Ihre Schnittstelle eine Methode definiert, muss sie auch vom Objektliteral definiert werden - sie ist nicht null. Das Objektliteral muss alles erfüllen, was die Schnittstelle definiert, sonst zeigt das Typsystem einen Fehler an.
Rick Love

290

Update 2019-05-15 (Verbessertes Codemuster als Alternative)

Nach vielen Jahren der Verwendung constund Nutzung von mehr Funktionscode würde ich in den meisten Fällen davon abraten, den folgenden Code zu verwenden. (Wenn Sie Objekte erstellen, ist es oft ein Hinweis darauf, dass etwas nicht stimmt, wenn Sie das Typsystem in einen bestimmten Typ zwingen, anstatt es auf Typen schließen zu lassen.)

Stattdessen würde ich empfehlen, constso viele Variablen wie möglich zu verwenden und dann das Objekt als letzten Schritt zusammenzustellen:

const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
  • Dadurch wird alles ordnungsgemäß eingegeben, ohne dass eine explizite Eingabe erforderlich ist.
  • Die Feldnamen müssen nicht erneut eingegeben werden.
  • Dies führt zu dem saubersten Code aus meiner Erfahrung.
  • Auf diese Weise kann der Compiler mehr Statusüberprüfungen durchführen. Wenn Sie beispielsweise an mehreren Orten zurückkehren, stellt der Compiler sicher, dass immer derselbe Objekttyp zurückgegeben wird. Dies ermutigt Sie, den gesamten Rückgabewert an jeder Position zu deklarieren Absicht dieses Wertes).

Ergänzung 2020-02-26

Wenn Sie tatsächlich einen Typ benötigen, den Sie träge initialisieren können: Markieren Sie, dass es sich um einen nullbaren Vereinigungstyp handelt (null oder Typ). Das Typsystem verhindert, dass Sie es verwenden, ohne vorher sicherzustellen, dass es einen Wert hat.

In tsconfig.json, stellen Sie sicher , können Sie strenge null Kontrollen:

"strictNullChecks": true

Verwenden Sie dann dieses Muster und lassen Sie sich vom Typsystem vor versehentlichem null / undefiniertem Zugriff schützen:



const state = {
    instance: null as null | ApiService,
    // OR
    // instance: undefined as undefined | ApiService,

};

const useApi = () => {
    // If I try to use it here, the type system requires a safe way to access it

    // Simple lazy-initialization 
    const api = state?.instance ?? (state.instance = new ApiService());
    api.fun();

    // Also here are some ways to only access it if it has value:

    // The 'right' way: Typescript 3.7 required
    state.instance?.fun();

    // Or the old way: If you are stuck before Typescript 3.7
    state.instance && state.instance.fun();

    // Or the long winded way because the above just feels weird
    if (state.instance) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans way
    if (state.instance != null) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans 
    // AND I was told to always use triple === in javascript even with null checks way
    if (state.instance !== null && state.instance !== undefined) { state.instance.fun(); }
};

class ApiService {
    fun() {
        // Do something useful here
    }
}

Führen Sie in 99% der Fälle Folgendes nicht aus:

Update 10.02.2016 - Umgang mit TSX (Danke @Josh)

Verwenden Sie den asOperator für TSX.

var obj = {
    property: null as string
};

Ein längeres Beispiel:

var call = {
    hasStarted: null as boolean,
    hasFinished: null as boolean,
    id: null as number,
};

Ursprüngliche Antwort

Verwenden Sie den Cast-Operator, um dies kurz zu machen (indem Sie null auf den gewünschten Typ setzen).

var obj = {
    property: <string> null
};

Ein längeres Beispiel:

var call = {
    hasStarted: <boolean> null,
    hasFinished: <boolean> null,
    id: <number> null,
};

Dies ist viel besser als zwei Teile (einer zum Deklarieren von Typen, der zweite zum Deklarieren von Standardwerten):

var callVerbose: {
    hasStarted: boolean;
    hasFinished: boolean;
    id: number;
} = {
    hasStarted: null,
    hasFinished: null,
    id: null,
};

10
Wenn Sie TSX (TS mit JSX) verwenden, können Sie die Benennung der spitzen Klammern nicht verwenden, sodass diese Zeilen zu etwas werden, property: null as stringbei dem der wichtige Unterschied der asOperator ist.
Josh

2
@RickLove Beschränkt dies tatsächlich den Typ der Objektvariablen oder ist dies nur eine Möglichkeit, die Typen bei der Zuweisung anzugeben? Mit anderen Worten, nachdem Sie der Variablen callin Ihrem zweiten Beispiel zugewiesen haben, können Sie ihr einen völlig anderen Typ zuweisen?
Daniel Griscom

3
Dies sollte Antwort sein
Drew R

4
funktioniert nicht. Error:(33, 15) TS2352:Type 'null' cannot be converted to type 'string'.
Slideshowp2

2
Ist dies nur ein Missbrauch eines Merkmals der Sprache oder ist dies tatsächlich legitim? Könnten Sie einen Link zum Lesen von offiziellen Dokumenten bereitstellen? Vielen Dank!
Qwerty

31

Ich bin überrascht , dass niemand die dies erwähnt , aber man konnte nur eine Schnittstelle namens erstellen ObjectLiteral, die akzeptiert key: valuePaare Typ string: any:

interface ObjectLiteral {
  [key: string]: any;
}

Dann würden Sie es so verwenden:

let data: ObjectLiteral = {
  hello: "world",
  goodbye: 1,
  // ...
};

Ein zusätzlicher Bonus ist, dass Sie diese Schnittstelle beliebig oft für beliebig viele Objekte wiederverwenden können.

Viel Glück.


3
Dies ist eine schlechte Idee, es macht das Typensystem wertlos. Der Zweck von Typoskript besteht darin, dem Typensystem zu ermöglichen, Fehler zu vermeiden und eine bessere Autovervollständigungsfunktionalität für Werkzeuge bereitzustellen - dies deaktiviert im Grunde alle Vorteile von Typeskript. Es ist besser, im obigen Beispiel keine Schnittstelle zu verwenden.
Rick Love

1
@ RickLove Ich bin absolut anderer Meinung. Dies war außerordentlich nützlich, wenn mehrere Eigenschaften optional sind, wir aber dennoch alle darin enthaltenen Typen klar definieren möchten (z. B. mit Argumenten für eine Funktion). Dies kann einfach als syntaktischer Zucker angesehen werden und funktioniert buchstäblich wie ein Spread-Operator für die Eigenschaften von Interfaces .
CPHPython

@CPHPython Warum nicht einfach die optionalen Parameter mit ihren spezifischen Typen angeben? Dies ist nur dann sinnvoll, wenn die Namen zum Zeitpunkt des Codes nicht bekannt sind (dh sie stammen aus einer Datenbank oder einer externen Quelle). Auch für komplexe Kombinationen von Argumenten funktionieren Unionstypen hervorragend. Wenn Sie gegen bestimmte Namen codieren, sollten diese nach Möglichkeit definiert werden. Wenn es nur Daten sind, die die Logik nicht beeinflussen, lassen Sie sie natürlich aus dem Typsystem aus.
Rick Love

1
@RickLove "Die Namen sind zur Codezeit nicht bekannt" -> Dies ist ein gutes und praktisches Beispiel. Die Werte dieser Schlüsseltypen sind bekannt und sie sind alle gleich (z . B. Zeichenfolge ). Denken Sie daran, dass ich nur die Verwendung des [key: string]Teils befürwortete , nicht irgendeinen Teil als Typdefinition des Werts ... Das würde in der Tat die Nützlichkeit des Typs beeinträchtigen.
CPHPython

1
Wie würde ich vorgehen, wenn ich zulassen möchte, dass Zeichenfolgen und Zahlen hinzugefügt werden, aber keine anderen Typen?
DonkeyBanana

14

Wenn Sie versuchen, eine Typanmerkung zu schreiben, lautet die Syntax:

var x: { property: string; } = ...;

Wenn Sie versuchen, ein Objektliteral zu schreiben, lautet die Syntax:

var x = { property: 'hello' };

Ihr Code versucht, einen Typnamen an einer Wertposition zu verwenden.


10
Du var x: { property: string; } = ...;bist ein Scherz! Ich hoffte, dass die Auslassungspunkte eine gültige Syntax waren var x: { property: string; } = { property: 'hello' };.
Jason Kleban

10

Wenn wir in TypeScript ein Objekt deklarieren, verwenden wir die folgende Syntax:

[access modifier] variable name : { /* structure of object */ }

Zum Beispiel:

private Object:{ Key1: string, Key2: number }

7

Wenn Sie versuchen, einem zerstörten Objektliteral Typisierungen hinzuzufügen , z. B. in Argumenten für eine Funktion, lautet die Syntax wie folgt:

function foo({ bar, baz }: { bar: boolean, baz: string }) {
  // ...
}

foo({ bar: true, baz: 'lorem ipsum' });

5

Wenn Ihre Eigenschaften denselben Typ haben, können Sie einen vordefinierten Dienstprogrammtyp verwenden Record:

type Keys = "property" | "property2"

var obj: Record<Keys, string> = {
  property: "my first prop",
  property2: "my second prop",
};

Sie können natürlich noch weiter gehen und einen benutzerdefinierten Typ für Ihre Eigenschaftswerte definieren:

type Keys = "property" | "property2"
type Value = "my prop" | "my other allowed prop"

var obj: Record<Keys, Value> = {
  property: "my prop",
  property2: "my second prop", // TS Error: Type '"my second prop"' is not assignable to type 'Value'.
};

2
Genau das, was ich brauchte. Vielen Dank! Typoskript für den Gewinn!
Audacitus

Ich würde auch letanstelle von varhier verwenden.
Fwd079

3
// Use ..

const Per = {
  name: 'HAMZA',
  age: 20,
  coords: {
    tele: '09',
    lan: '190'
  },
  setAge(age: Number): void {
    this.age = age;
  },
  getAge(): Number {
    return age;
  }
};
const { age, name }: { age: Number; name: String } = Per;
const {
  coords: { tele, lan }
}: { coords: { tele: String; lan: String } } = Per;

console.log(Per.getAge());

3
Hey und willkommen bei SO! Können Sie Ihre Antwort überhaupt erweitern, wäre es hilfreich zu erklären, wie / warum dies funktioniert.
Party-Ring

1

In Ihrem Code:

var obj = {
  myProp: string;
};

Sie erstellen tatsächlich ein Objektliteral und weisen der Eigenschaft myProp die Variablenzeichenfolge zu. Obwohl dies eine sehr schlechte Praxis ist, wäre dies tatsächlich ein gültiger TS-Code (verwenden Sie diesen nicht!):

var string = 'A string';

var obj = {
  property: string
};

Sie möchten jedoch, dass das Objektliteral eingegeben wird. Dies kann auf verschiedene Arten erreicht werden:

Schnittstelle:

interface myObj {
    property: string;
}

var obj: myObj = { property: "My string" };

Benutzerdefinierter Typ:

type myObjType = {
    property: string
};

var obj: myObjType = { property: "My string" };

Darsteller:

var obj = {
    myString: <string> 'hi',
    myNumber: <number> 132,
};

Objekttyp-Literal:

var obj: { property: string; } = { property: "Mystring" };
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.