Come posso assegnare dinamicamente le proprietà a un oggetto in TypeScript?


359

Se volessi assegnare programmaticamente una proprietà a un oggetto in Javascript, lo farei così:

var obj = {};
obj.prop = "value";

Ma in TypeScript, questo genera un errore:

La proprietà 'prop' non esiste sul valore di tipo '{}'

Come posso assegnare una nuova proprietà a un oggetto in TypeScript?


1
FYI. Ho aperto una discussione relativa a questo se desideri seguirla.
joshuapoehls,

Risposte:


456

È possibile indicare objcome any, ma ciò vanifica l'intero scopo dell'uso del dattiloscritto. obj = {}implica objè un Object. Contrassegnarlo come anynon ha senso. Per ottenere la consistenza desiderata, un'interfaccia potrebbe essere definita come segue.

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

O per renderlo compatto:

var obj: {[k: string]: any} = {};

LooseObjectpuò accettare campi con qualsiasi stringa come chiave e anydigitare come valore.

obj.prop = "value";
obj.prop2 = 88;

La vera eleganza di questa soluzione è che puoi includere campi typesafe nell'interfaccia.

interface MyType {
    typesafeProp1?: number,
    requiredProp1: string,
    [key: string]: any
}

var obj: MyType ;
obj = { requiredProp1: "foo"}; // valid
obj = {} // error. 'requiredProp1' is missing
obj.typesafeProp1 = "bar" // error. typesafeProp1 should be a number

obj.prop = "value";
obj.prop2 = 88;

Mentre questo risponde alla domanda originale, la risposta qui di @GreeneCreations potrebbe dare un'altra prospettiva su come affrontare il problema.


13
Penso che questa sia la soluzione migliore ora. Penso che al momento fosse stata posta la domanda, le proprietà dell'indice come questa non erano ancora implementate in TypeScript.
Peter Olson,

che ne dite di un oggetto nativo come Error
Wayou

@Wayou oggetti nativi come Error e Array ereditano da Object. Quindi LooseObjectpuò prendere anche un errore.
Akash Kurian Jose,

Puoi verificare se l'oggetto ha una certa proprietà (dinamica)?
phhbr,

1
per questi oggetti sciolti, come li scrivi in ​​forma di getter-setter?
JokingBatman,

80

O tutto in una volta sola:

  var obj:any = {}
  obj.prop = 5;

42
Che senso ha TypeScript se devo lanciare così tante cose anyper usarlo? Diventa solo un rumore extra nel mio codice ..: /
AjaxLeung

16
@AjaxLeung Se devi farlo, lo stai usando in modo sbagliato.
Alex

2
Vedi la modifica aggiuntiva sopra che conserva l'oggetto tipo precedente.
Andre Vianna,

6
@AjaxLeung Dovresti lanciare molto raramente any. TypeScript viene utilizzato per rilevare (potenziali) errori in fase di compilazione. Se esegui il cast per anysilenziare gli errori, perdi il potere di scrivere e potresti anche tornare al JS puro. anydovrebbe idealmente essere utilizzato solo se si importa codice per il quale non è possibile scrivere definizioni TS o durante la migrazione del codice da JS a TS
Precastic

63

Tendo a mettere anydall'altra parte, var foo:IFoo = <any>{};quindi qualcosa del genere è ancora dilemma:

interface IFoo{
    bar:string;
    baz:string;
    boo:string;     
}

// How I tend to intialize 
var foo:IFoo = <any>{};

foo.bar = "asdf";
foo.baz = "boo";
foo.boo = "boo";

// the following is an error, 
// so you haven't lost type safety
foo.bar = 123; 

In alternativa è possibile contrassegnare queste proprietà come facoltative:

interface IFoo{
    bar?:string;
    baz?:string;
    boo?:string;    
}

// Now your simple initialization works
var foo:IFoo = {};

Provalo online


5
+1 per essere l'unica soluzione che mantiene la sicurezza del tipo. Assicurati di istanziare tutte le proprietà non opzionali subito dopo, per evitare che i bug ti mordano in seguito.
Aidiakapi,

Funziona davvero? Dopo la compilazione ho ancora <any> {} nel mio javascript.
bvs

4
I still have <any>{allora non hai compilato. TypeScript lo rimuoverebbe nel suo emit
basarat il

4
In questi giorni, var foo: IFoo = {} as anyè preferito. La vecchia sintassi per il typecasting si scontrava con TSX (Typescript-ified JSX).
Don

51

Questa soluzione è utile quando l'oggetto ha un tipo specifico. Come quando si ottiene l'oggetto su un'altra fonte.

let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.

7
Questa è la soluzione corretta per incarichi una tantum.
soundly_typed il

Questa risposta ha funzionato per me perché per l'accesso a Facebook ho dovuto aggiungere proprietà all'oggetto finestra . Il mio primo utilizzo della parola chiave as in TypeScript.
AlanObject

33

Anche se il compilatore si lamenta, dovrebbe comunque produrlo come richiesto. Tuttavia, questo funzionerà.

var s = {};
s['prop'] = true;

2
sì bene, questo non è davvero un modo di scrivere script, perdi l'intelligenza.
pregmatch

26

Un'ulteriore opzione è quella di accedere alla proprietà come una raccolta:

var obj = {};
obj['prop'] = "value";


2
Questo è il modo più conciso. Object.assign(obj, {prop: "value"})anche da ES6 / ES2015.
Charles,

9

Sono sorpreso che nessuna delle risposte faccia riferimento a Object.assign poiché questa è la tecnica che utilizzo ogni volta che penso a "composizione" in JavaScript.

E funziona come previsto in TypeScript:

interface IExisting {
    userName: string
}

interface INewStuff {
    email: string
}

const existingObject: IExisting = {
    userName: "jsmith"
}

const objectWithAllProps: IExisting & INewStuff = Object.assign({}, existingObject, {
    email: "jsmith@someplace.com"
})

console.log(objectWithAllProps.email); // jsmith@someplace.com

vantaggi

  • digitare sicurezza perché non è necessario utilizzare il anytipo affatto
  • usa il tipo aggregato di TypeScript (come indicato da &quando si dichiara il tipo di objectWithAllProps), che comunica chiaramente che stiamo componendo un nuovo tipo al volo (cioè dinamicamente)

Cose di cui essere consapevoli

  1. Object.assign ha i suoi aspetti unici (che sono ben noti agli sviluppatori JS più esperti) che dovrebbero essere considerati quando si scrive TypeScript.
    • Può essere usato in modo mutevole o in modo immutabile (ho dimostrato il modo immutabile sopra, il che significa che existingObjectrimane intatto e quindi non ha una emailproprietà. Per la maggior parte dei programmatori di stile funzionale, questa è una buona cosa poiché il risultato è l'unica nuova modifica).
    • Object.assign funziona al meglio quando si hanno oggetti più piatti. Se si stanno combinando due oggetti nidificati che contengono proprietà nullable, è possibile finire per sovrascrivere i valori di verità con undefined. Se stai attento all'ordine degli argomenti Object.assign, dovresti andare bene.

Per me, nei casi in cui è necessario utilizzarlo per visualizzare i dati, sembra che funzioni correttamente, ma quando è necessario aggiungere voci di tipi modificati a un array, questo non sembra funzionare troppo bene. Ho fatto ricorso alla composizione dinamica dell'oggetto e all'assegnazione in una riga, quindi all'assegnazione delle proprietà dinamiche in righe successive. Questo mi ha portato per lo più lì, quindi grazie.
Shafiq Jetha,

8

È possibile creare un nuovo oggetto basato sul vecchio oggetto utilizzando l'operatore diffusione

interface MyObject {
    prop1: string;
}

const myObj: MyObject = {
    prop1: 'foo',
}

const newObj = {
    ...myObj,
    prop2: 'bar',
}

console.log(newObj.prop2); // 'bar'

TypeScript inferirà tutti i campi dell'oggetto originale e VSCode eseguirà il completamento automatico, ecc.


bello, ma nel caso in cui sia necessario utilizzare prop2 ad esempio prop3, sarà difficile da implementare
ya_dimon

6

Il più semplice seguirà

const obj = <any>{};
obj.prop1 = "value";
obj.prop2 = "another value"

5

È possibile aggiungere un membro a un oggetto esistente tramite

  1. allargando il tipo (leggi: estendi / specializza l'interfaccia)
  2. cast l'oggetto originale nel tipo esteso
  3. aggiungi il membro all'oggetto
interface IEnhancedPromise<T> extends Promise<T> {
    sayHello(): void;
}

const p = Promise.resolve("Peter");

const enhancedPromise = p as IEnhancedPromise<string>;

enhancedPromise.sayHello = () => enhancedPromise.then(value => console.info("Hello " + value));

// eventually prints "Hello Peter"
enhancedPromise.sayHello();

1
la migliore soluzione per me. Ho imparato qualcosa di nuovo oggi, grazie
Maurice,

ottengo l'errore TS2352 con questo codice
ya_dimon

@ya_dimon provalo sul Playground di TypeScript: typescriptlang.org/play Funziona benissimo!
Daniel Dietrich,

@DanielDietrich ora funziona .. grazie, non ho idea di cosa sia successo.
ya_dimon,

prego! il turno accade;)
Daniel Dietrich il

5

Dal momento che non puoi farlo:

obj.prop = 'value';

Se il tuo compilatore TS e la tua linter non ti restringono, puoi scrivere questo:

obj['prop'] = 'value';

Se il tuo compilatore o linter TS è rigoroso, un'altra risposta sarebbe quella di digitare:

var obj = {};
obj = obj as unknown as { prop: string };
obj.prop = "value";

3
Questo è se 'noImplicitAny: false' su tsconfig.json
LEMUEL ADANE

Altrimenti puoi rispondere a GreeneCreations.
LEMUEL ADANE,

4

Memorizza qualsiasi nuova proprietà su qualsiasi tipo di oggetto digitandolo in "qualsiasi":

var extend = <any>myObject;
extend.NewProperty = anotherObject;

Successivamente puoi recuperarlo riportando l'oggetto esteso su "any":

var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;

Questa è totalmente la soluzione corretta. Diciamo che hai un oggetto let o: ObjectType; .... più tardi puoi lanciare o su qualsiasi (<any> o) .newProperty = 'foo'; e può essere recuperato come (<any> o) .newProperty. Nessun errore del compilatore e funziona come un fascino.
Matt Ashley,

questa frenata intellisense ... c'è un modo per farlo se non per mantenere l'intellisense?
pregmatch

4

Per garantire che il tipo sia un Object(ovvero coppie chiave-valore ), utilizzare:

const obj: {[x: string]: any} = {}
obj.prop = 'cool beans'

4
  • Caso 1:

    var car = {type: "BMW", model: "i8", color: "white"}; car['owner'] = "ibrahim"; // You can add a property:

  • Caso 2:

    var car:any = {type: "BMW", model: "i8", color: "white"}; car.owner = "ibrahim"; // You can set a property: use any type


4

puoi usare questo:

this.model = Object.assign(this.model, { newProp: 0 });

3

È possibile aggiungere questa dichiarazione per mettere a tacere gli avvisi.

declare var obj: any;


3

La migliore pratica è usare la digitazione sicura, ti consiglio di:

interface customObject extends MyObject {
   newProp: string;
   newProp2: number;
}

3

Per preservare il tipo precedente, esegui il cast temporaneo dell'oggetto su qualsiasi

  var obj = {}
  (<any>obj).prop = 5;

La nuova proprietà dinamica sarà disponibile solo quando si utilizza il cast:

  var a = obj.prop; ==> Will generate a compiler error
  var b = (<any>obj).prop; ==> Will assign 5 to b with no error;

3

Ecco una versione speciale di Object.assign, che regola automaticamente il tipo di variabile ad ogni cambio di proprietà. Non sono necessarie variabili aggiuntive, asserzioni di tipo, tipi espliciti o copie di oggetti:

function assign<T, U>(target: T, source: U): asserts target is T & U {
    Object.assign(target, source)
}

const obj = {};
assign(obj, { prop1: "foo" })
//  const obj now has type { prop1: string; }
obj.prop1 // string
assign(obj, { prop2: 42 })
//  const obj now has type { prop1: string; prop2: number; }
obj.prop2 // number

//  const obj: { prop1: "foo", prop2: 42 }

Nota: l' esempio utilizza le funzioni di asserzione TS 3.7 . Il tipo di ritorno di assignè void, a differenza Object.assign.


1

Se stai usando Typescript, presumibilmente vuoi usare il tipo di sicurezza; nel qual caso l'Oggetto nudo e 'qualsiasi' sono controindicati.

Meglio non usare Object o {}, ma un certo tipo di nome; oppure potresti utilizzare un'API con tipi specifici, che devi estendere con i tuoi campi. Ho trovato che questo funziona:

class Given { ... }  // API specified fields; or maybe it's just Object {}

interface PropAble extends Given {
    props?: string;  // you can cast any Given to this and set .props
    // '?' indicates that the field is optional
}
let g:Given = getTheGivenObject();
(g as PropAble).props = "value for my new field";

// to avoid constantly casting: 
let k:PropAble = getTheGivenObject();
k.props = "value for props";

1

assegnare dinamicamente le proprietà a un oggetto in TypeScript.

per farlo Devi solo usare le interfacce dattiloscritte in questo modo:

interface IValue {
    prop1: string;
    prop2: string;
}

interface IType {
    [code: string]: IValue;
}

puoi usarlo così

var obj: IType = {};
obj['code1'] = { 
    prop1: 'prop 1 value', 
    prop2: 'prop 2 value' 
};

1
Provo con il tuo codice, ma non ricevo alcun errore: pastebin.com/NBvJifzN
pablorsk,

1
prova a inizializzare il attributescampo all'interno SomeClass e che dovrebbe risolverlo public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid

1

Prova questo:

export interface QueryParams {
    page?: number,
    limit?: number,
    name?: string,
    sort?: string,
    direction?: string
}

Quindi usalo

const query = {
    name: 'abc'
}
query.page = 1

0

L'unica soluzione completamente sicura per i tipi è questa , ma è un po 'prolissa e ti costringe a creare più oggetti.

Se devi prima creare un oggetto vuoto, scegli una di queste due soluzioni. Tieni presente che ogni volta che usi asperdi sicurezza.

Soluzione più sicura

Il tipo di objectè sicuro dentro getObject, il che significa che object.asarà di tipostring | undefined

interface Example {
  a: string;
  b: number;
}

function getObject() {
  const object: Partial<Example> = {};
  object.a = 'one';
  object.b = 1;
  return object as Example;
}

Soluzione breve

Il tipo di nonobject è sicuro all'interno getObject, il che significa che object.asarà di tipo stringanche prima del suo incarico.

interface Example {
  a: string;
  b: number;
}

function getObject() {
  const object = {} as Example;
  object.a = 'one';
  object.b = 1;
  return object;
}
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.