TypeScript e inizializzatori di campo

Come iniziare una nuova class in TS in questo modo (esempio in C# per mostrare ciò che voglio):

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

SOLUZIONE:
Sintassi JavaScript classica:

 return { Field1: "ASD", Field2: "QWE" }; 

C’è un problema nel codeplex TypeScript che descrive questo: Supporto per gli inizializzatori di oggetti .

Come già detto, puoi già farlo usando le interfacce in TypeScript invece delle classi:

 interface Name { first: string; last: string; } class Person { name: Name; age: number; } var bob: Person = { name: { first: "Bob", last: "Smith", }, age: 35, }; 

Aggiornato il 07/12/2016: Typescript 2.1 introduce i Tipi mappati e fornisce il Partial , che ti permette di farlo ….

 class Person { public name: string = "default" public address: string = "default" public age: number = 0; public constructor(init?:Partial) { Object.assign(this, init); } } let persons = [ new Person(), new Person({}), new Person({name:"John"}), new Person({address:"Earth"}), new Person({age:20, address:"Earth", name:"John"}), ]; 

Risposta originale:

Il mio approccio è definire una variabile di fields separata che passi al costruttore. Il trucco è ridefinire tutti i campi di class per questo inizializzatore come facoltativo. Quando l’object viene creato (con i suoi valori predefiniti), si assegna semplicemente l’object inizializzatore a this ;

 export class Person { public name: string = "default" public address: string = "default" public age: number = 0; public constructor( fields?: { name?: string, address?: string, age?: number }) { if (fields) Object.assign(this, fields); } } 

o farlo manualmente (un po ‘più sicuro):

 if (fields) { this.name = fields.name || this.name; this.address = fields.address || this.address; this.age = fields.age || this.age; } 

utilizzo:

 let persons = [ new Person(), new Person({name:"Joe"}), new Person({ name:"Joe", address:"planet Earth" }), new Person({ age:5, address:"planet Earth", name:"Joe" }), new Person(new Person({name:"Joe"})) //shallow clone ]; 

e uscita della console:

 Person { name: 'default', address: 'default', age: 0 } Person { name: 'Joe', address: 'default', age: 0 } Person { name: 'Joe', address: 'planet Earth', age: 0 } Person { name: 'Joe', address: 'planet Earth', age: 5 } Person { name: 'Joe', address: 'default', age: 0 } 

Questo ti dà sicurezza di base e inizializzazione della proprietà, ma è tutto opzionale e può essere fuori ordine. Le impostazioni predefinite della class vengono lasciate da sole se non si passa un campo.

Puoi anche mescolarlo con i parametri richiesti del costruttore – i fields stick alla fine.

Più vicino allo stile C # come penserete ( la syntax di init-field effettiva è stata rifiutata ). Preferisco di gran lunga l’inizializzatore di campo corretto, ma non sembra che accadrà ancora.

Per il confronto, se si utilizza l’approccio di casting, l’object initialiser deve avere TUTTI i campi per il tipo a cui si sta eseguendo il cast, inoltre non ottenere funzioni specifiche della class (o derivazioni) create dalla class stessa.

Puoi influenzare un object anonimo castato nel tuo tipo di class. Bonus : in Visual Studio, beneficerai dell’intelligenza in questo modo 🙂

 var anInstance: AClass =  { Property1: "Value", Property2: "Value", PropertyBoolean: true, PropertyNumber: 1 }; 

Modificare:

ATTENZIONE Se la class ha metodi, l’istanza della class non li otterrà e il costruttore non verrà eseguito.

Questa soluzione dovrebbe essere utilizzata solo con l’interfaccia . Ad esempio, utilizzare questa soluzione per memorizzare il modello come Plain Old Object.

 interface IClass { Property1: string; Property2: string; PropertyBoolean: boolean; PropertyNumber: number; } var anObject: IClass =  { Property1: "Value", Property2: "Value", PropertyBoolean: true, PropertyNumber: 1 }; 

In alcuni scenari può essere accettabile utilizzare Object.create . Il riferimento Mozilla include un polyfill se hai bisogno di compatibilità retrotriggers o vuoi eseguire la tua funzione di inizializzazione.

Applicato al tuo esempio:

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

Scenari utili

  • Test di unità
  • Dichiarazione in linea

Nel mio caso ho trovato questo utile nei test unitari per due ragioni:

  1. Quando si testano le aspettative, spesso desidero creare un object snello come un’aspettativa
  2. I framework di test unitari (come Jasmine) possono confrontare il prototipo dell’object ( __proto__ ) e fallire il test. Per esempio:
 var actual = new MyClass(); actual.field1 = "ASD"; expect({ field1: "ASD" }).toEqual(actual); // fails 

L’output del fallimento del test dell’unità non darà un’idea di ciò che non corrisponde.

  1. Nei test unitari posso essere selettivo su quali browser supporto

Infine, la soluzione proposta su http://typescript.codeplex.com/workitem/334 non supporta la dichiarazione in linea di JSON. Ad esempio, il seguente non si compila:

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

Sarei più propenso a farlo in questo modo, usando (facoltativamente) proprietà e valori predefiniti automatici. Non hai suggerito che i due campi siano parte di una struttura dati, ecco perché ho scelto in questo modo.

Potresti avere le proprietà sulla class e assegnarle nel solito modo. E ovviamente possono o non possono essere richiesti, quindi è anche qualcos’altro. È solo che questo è un bel zucchero sintattico.

 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 

Suggerisco un approccio che non richiede Typescript 2.1:

 class Person { public name: string; public address?: string; public age: number; public constructor(init:Person) { Object.assign(this, init); } public someFunc() { // todo } } let person = new Person({ age:20, name:"John" }); person.someFunc(); 

punti chiave:

  • Typescript 2.1 non richiesto, Partial non richiesto
  • Supporta funzioni (rispetto alla semplice asserzione di tipo che non supporta le funzioni)

Di seguito è riportata una soluzione che combina un’applicazione più breve di Object.assign per Object.assign più attentamente il modello C# originale.

Ma prima, esaminiamo le tecniche offerte finora, che includono:

  1. Copia i costruttori che accettano un object e lo applicano a Object.assign
  2. Un astuto trucco Partial all’interno del costruttore di copie
  3. Uso di “casting” contro un POJO
  4. Object.create di Object.assign anziché Object.assign

Naturalmente, ognuno ha i suoi pro / contro. La modifica di una class di destinazione per creare un costruttore di copie potrebbe non essere sempre un’opzione. E “casting” perde tutte le funzioni associate al tipo di target. Object.create sembra meno attraente dal momento che richiede una mappa descrittore di proprietà piuttosto dettagliata.

La risposta più breve e generale

Quindi, ecco un altro approccio che è in qualche modo più semplice, mantiene la definizione del tipo e i prototipi di funzione associati, e modella più da vicino il modello C# desiderato:

 const john = Object.assign( new Person(), { name: "John", age: 29, address: "Earth" }); 

Questo è tutto. L’unica aggiunta sul modello C# è Object.assign con 2 parentesi e una virgola. Guarda l’esempio di lavoro qui sotto per confermare che mantiene i prototipi di funzione del tipo. Nessun costruttore richiesto e nessun trucco intelligente.

Esempio di lavoro

Questo esempio mostra come inizializzare un object usando un’approssimazione di un inizializzatore del campo C# :

 class Person { name: string = ''; address: string = ''; age: number = 0; aboutMe() { return `Hi, I'm ${this.name}, aged ${this.age} and from ${this.address}`; } } // typescript field initializer (maintains "type" definition) const john = Object.assign( new Person(), { name: "John", age: 29, address: "Earth" }); // initialized object maintains aboutMe() function prototype console.log( john.aboutMe() ); 

Il modo più semplice per farlo è con il casting di tipo.

 return { Field1: "ASD", Field2: "QWE" }; 

Se stai usando una vecchia versione di typescript <2.1 allora puoi usare simile al seguente, che fondamentalmente è il cast di qualsiasi oggetto digitato:

 const typedProduct = { code: product.sku }; 

NOTA: l’utilizzo di questo metodo è utile solo per i modelli di dati poiché rimuoverà tutti i metodi nell’object. Fondamentalmente consiste nel lanciare qualsiasi object su un object digitato

Potrebbe anche trasmettere usando:

  return { Field1: "ASD", Field2: "QWE" } as MyClass;