Digitare la definizione nell'oggetto letterale in TypeScript


346

Nelle classi TypeScript è possibile dichiarare i tipi per le proprietà, ad esempio:

class className {
  property: string;
};

Come dichiarare il tipo di una proprietà in un oggetto letterale?

Ho provato il seguente codice ma non viene compilato:

var obj = {
  property: string;
};

Ricevo il seguente errore:

Il nome 'stringa' non esiste nell'ambito corrente

Sto facendo qualcosa di sbagliato o è un bug?

Risposte:


424

Sei abbastanza vicino, devi solo sostituire il =con un :. È possibile utilizzare un tipo di oggetto letterale (vedere la sezione 3.5.3 delle specifiche) o un'interfaccia. L'uso di un tipo di oggetto letterale è simile a quello che hai:

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

Ma puoi anche usare un'interfaccia

interface MyObjLayout {
    property: string;
}

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

14
L'opzione DRY (Don't Repeat Yourself) di @Rick Love con l'operatore del cast sembra molto più chiara. Sto solo commentando, non sottovalutando ...
spettro il

So che è stata data risposta qualche tempo fa, ma mi sbaglio nelle ipotesi che i problemi possono sorgere quando la classe ha metodi e proprietà? Poiché la classe non viene inizializzata e assegniamo solo proprietà, la chiamata di un metodo sulla classe causerà un'eccezione nulla. Fondamentalmente, l'oggetto che creiamo 'agisce' solo come classe perché assegniamo il suo tipo, ma in realtà non è un'istanza di quella classe. Vale a dire che dobbiamo creare la classe con la parola chiave "nuova". E poi torniamo al punto 1, dato che non possiamo fare qualcosa come new class () {prop: 1}; in TS come possiamo in C #, per esempio.
DeuxAlpha,

@DeuxAlpha Si sta assegnando a un'interfaccia , che non può avere metodi. Non credo che tu possa assegnarti a un corso come questo.
Mog0

il collegamento alle specifiche sarebbe bello :)
tra il

@DeuxAlpha sta creando un oggetto letterale, non un oggetto di classe. Anche un'interfaccia può avere metodi. Se la tua interfaccia definisce un metodo, anche l'oggetto letterale deve definirlo - non sarà nullo. L'oggetto letterale deve soddisfare tutto ciò che l'interfaccia definisce o il sistema di tipi mostrerà un errore.
Rick Love,

291

Aggiornamento 15/05/2019 (modello di codice migliorato come alternativa)

Dopo molti anni di utilizzo conste di utilizzo di un codice più funzionale, nella maggior parte dei casi consiglierei di non utilizzare quanto segue. (Quando si costruiscono oggetti, forzare il sistema di tipi in un tipo specifico invece di lasciarne dedurre i tipi è spesso un'indicazione che qualcosa non va).

Invece, consiglierei di utilizzare le constvariabili il più possibile e quindi comporre l'oggetto come passaggio finale:

const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
  • Questo scriverà correttamente tutto senza la necessità di digitare esplicitamente.
  • Non è necessario digitare nuovamente i nomi dei campi.
  • Questo porta al codice più pulito dalla mia esperienza.
  • Ciò consente al compilatore di fornire una maggiore verifica dello stato (ad esempio, se si ritorna in più posizioni, il compilatore assicurerà che venga sempre restituito lo stesso tipo di oggetto, il che ti incoraggia a dichiarare l'intero valore di ritorno in ciascuna posizione) fornendo un risultato perfettamente chiaro intenzione di quel valore).

Aggiunta 2020-02-26

Se in realtà hai bisogno di un tipo che puoi inizializzare pigramente: Segna come un tipo di unione nullable (null o Type). Il sistema dei tipi ti impedirà di usarlo senza prima assicurarti che abbia un valore.

In tsconfig.json, assicurati di abilitare controlli null rigorosi:

"strictNullChecks": true

Quindi utilizzare questo modello e consentire al sistema di tipi di proteggerti da accessi nulli / indefiniti accidentali:



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

Non eseguire le operazioni seguenti nel 99% dei casi:

Aggiornamento 10-02-2016 - Per gestire TSX (Grazie @Josh)

Utilizzare l' asoperatore per TSX.

var obj = {
    property: null as string
};

Un esempio più lungo:

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

Risposta originale

Usa l'operatore cast per rendere questo succinto (lanciando null al tipo desiderato).

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

Un esempio più lungo:

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

È molto meglio che avere due parti (una per dichiarare i tipi, la seconda per dichiarare i valori predefiniti):

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

10
Se si utilizza TSX (TS con JSX), non è possibile utilizzare la denominazione della parentesi angolare, quindi quelle linee diventano qualcosa di simile in property: null as stringcui la differenza importante è l' asoperatore.
Josh,

2
@RickLove In realtà questo limita il tipo della variabile oggetto o è solo un modo per specificare i tipi quando assegnati? In altre parole, dopo aver assegnato alla variabile callnel tuo secondo esempio, puoi assegnargli un tipo completamente diverso?
Daniel Griscom,

3
Questa dovrebbe essere una risposta
Drew R

4
non funziona. Error:(33, 15) TS2352:Type 'null' cannot be converted to type 'string'.
slideshowp2

2
È solo un abuso di una caratteristica della lingua o è in realtà Legit? Potresti fornire un link per leggere i documenti ufficiali? Grazie!
Qwerty,

31

Sono sorpreso che nessuno abbia menzionato questo, ma potresti semplicemente creare un'interfaccia chiamata ObjectLiteral, che accetta key: valuecoppie di tipo string: any:

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

Quindi lo useresti, in questo modo:

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

Un ulteriore vantaggio è che puoi riutilizzare questa interfaccia tutte le volte che vuoi, su tutti gli oggetti che desideri.

In bocca al lupo.


3
Questa è una cattiva idea, rende inutile il sistema dei tipi. Il punto di dattiloscritto è quello di consentire al sistema di tipi di aiutare a prevenire errori e anche di fornire una migliore funzionalità di completamento automatico degli strumenti - questo sostanzialmente disabilita tutti i vantaggi di Typescript. Sarebbe meglio non usare alcuna interfaccia nell'esempio sopra.
Rick Love,

1
@RickLove Non sono assolutamente d'accordo. Ciò è stato eccezionalmente utile quando diverse proprietà sono opzionali ma vogliamo comunque definire chiaramente tutti i tipi all'interno (ad es. Contenenti argomenti per una funzione). Questo potrebbe essere semplicemente visto come zucchero sintattico e funziona letteralmente come un operatore di diffusione per le proprietà di Interfaces .
CPHPython,

@CPHPython perché non specificare semplicemente i parametri opzionali con i loro tipi specifici? L'unica volta che ciò ha senso è se i nomi non sono noti al momento del codice (ovvero provengono da un database o da una fonte esterna). Anche per combinazioni complesse di argomenti, i tipi di unione funzionano alla grande. Se stai codificando con nomi specifici, dovrebbero essere definiti se possibile. Se sono solo i dati che non influiscono sulla logica, ovviamente lasciarli fuori dal sistema dei tipi.
Rick Love,

1
@RickLove "i nomi non sono noti al momento del codice" -> questo è un buon esempio pratico, i valori di quei tipi di chiavi sono noti e sono tutti uguali (es. Stringa ). Ricorda che stavo solo sostenendo l'uso della [key: string]parte, non di qualsiasi parte come definizione del tipo di valore ... Ciò avrebbe effettivamente eliminato l'utilità dei tipi.
CPHPython,

1
Come farei se volessi consentire l'aggiunta di stringhe e numeri ma non altri tipi?
DonkeyBanana,

14

Se stai cercando di scrivere un'annotazione di tipo, la sintassi è:

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

Se stai provando a scrivere un oggetto letterale, la sintassi è:

var x = { property: 'hello' };

Il codice sta tentando di utilizzare un nome di tipo in una posizione di valore.


10
La tua var x: { property: string; } = ...;è una presa in giro! Speravo che i puntini di sospensione fossero una sintassi valida per essere una scorciatoia var x: { property: string; } = { property: 'hello' };.
Jason Kleban,

10

In TypeScript se dichiariamo un oggetto, utilizziamo la sintassi seguente:

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

Per esempio:

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

7

Se si sta tentando di aggiungere letteralmente un oggetto destrutturato , ad esempio negli argomenti di una funzione, la sintassi è:

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

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

5

Se le proprietà hanno lo stesso tipo, è possibile utilizzare un tipo di utilità predefinito Record :

type Keys = "property" | "property2"

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

Ovviamente puoi andare oltre e definire un tipo personalizzato per i tuoi valori di proprietà:

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
Esattamente quello di cui avevo bisogno. Grazie! Dattiloscritto per la vittoria!
Audacitus

Vorrei anche usare al letposto di varqui.
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
Ciao e benvenuto in SO! Puoi espandere la tua risposta, sarebbe utile spiegare come / perché funziona.
anello per feste

1

Nel tuo codice:

var obj = {
  myProp: string;
};

In realtà stai creando un oggetto letterale e assegnando la stringa variabile alla proprietà myProp. Anche se una cattiva pratica sarebbe effettivamente un codice TS valido (non usarlo!):

var string = 'A string';

var obj = {
  property: string
};

Tuttavia, ciò che vuoi è che l'oggetto letterale sia digitato. Ciò può essere ottenuto in vari modi:

Interfaccia:

interface myObj {
    property: string;
}

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

Tipo personalizzato:

type myObjType = {
    property: string
};

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

Operatore cast:

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

Tipo di oggetto letterale:

var obj: { property: string; } = { property: "Mystring" };
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.