¿Cómo asigno dinámicamente propiedades a un objeto en TypeScript?

359

Si quisiera asignar programáticamente una propiedad a un objeto en Javascript, lo haría así:

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

Pero en TypeScript, esto genera un error:

La propiedad 'prop' no existe en el valor del tipo '{}'

¿Cómo se supone que debo asignar cualquier propiedad nueva a un objeto en TypeScript?

Peter Olson
fuente
1
FYI. He abierto una discusión relacionada con esto si desea seguirla.
joshuapoehls

Respuestas:

456

Es posible denotar objcomo any, pero eso anula el propósito de usar el mecanografiado. obj = {}implica objes un Object. Marcarlo como anyno tiene sentido. Para lograr la consistencia deseada, se podría definir una interfaz de la siguiente manera.

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

O para hacerlo compacto:

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

LooseObjectpuede aceptar campos con cualquier cadena como clave y anyescribir como valor.

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

La verdadera elegancia de esta solución es que puede incluir campos seguros en la interfaz.

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;

Si bien esto responde a la pregunta Original, la respuesta aquí por @GreeneCreations podría dar otra perspectiva sobre cómo abordar el problema.

Akash Kurian Jose
fuente
13
Creo que esta es la mejor solución ahora. Creo que en el momento en que se hizo la pregunta, propiedades de índice como esta aún no se implementaron en TypeScript.
Peter Olson
¿qué tal el objeto nativo como Error?
Wayou
Los objetos nativos de @Wayou como Error y Array heredan de Object. Por LooseObjectlo tanto, puede tomar un error también.
Akash Kurian Jose
¿Puede verificar si el objeto tiene una determinada propiedad (dinámica)?
phhbr
1
para estos objetos sueltos, ¿cómo los escribes en forma de getter-setter?
JokingBatman
80

O todo de una vez:

  var obj:any = {}
  obj.prop = 5;
Crwth
fuente
42
¿Cuál es el punto de TypeScript si tengo que emitir tantas cosas para anypoder usarlo? Simplemente se convierte en ruido extra en mi código ..: /
AjaxLeung
16
@AjaxLeung Si necesita hacer eso, lo está usando mal.
Alex
2
Vea la edición adicional anterior que conserva el tipo de objeto anterior.
Andre Vianna
66
@AjaxLeung Muy rara vez deberías enviar contenido any. TypeScript se utiliza para detectar errores (potenciales) en tiempo de compilación. Si echas a anysilenciar errores, entonces pierdes el poder de escribir y también puedes volver a JS puro. anyidealmente debería usarse solo si está importando código para el que no puede escribir definiciones de TS o mientras migra su código de JS a TS
Precastic
63

Tiendo a poner anydel otro lado, es decir, var foo:IFoo = <any>{};algo como esto sigue siendo seguro:

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; 

Alternativamente, puede marcar estas propiedades como opcionales:

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

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

Pruébalo en línea

basarat
fuente
55
+1 por ser la única solución que mantiene la seguridad de los tipos. Solo asegúrese de instanciar todas las propiedades no opcionales directamente después de ella, para evitar errores que lo muerdan más adelante.
Aidiakapi
¿Esto realmente funciona? Después de compilar, todavía tengo <any> {} en mi javascript.
bvs
44
I still have <any>{entonces no has compilado. TypeScript eliminaría eso en su emisión
basarat
44
En estos días, var foo: IFoo = {} as anyse prefiere. La antigua sintaxis para la conversión de tipos colisionaba con TSX (Typecript-ified JSX).
Don
51

Esta solución es útil cuando su objeto tiene Tipo específico. Como al obtener el objeto a otra fuente.

let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
jmvtrinidad
fuente
77
Esta es la solución correcta para tareas únicas.
soundly_typed
Esta respuesta funcionó para mí porque para iniciar sesión en Facebook tuve que agregar propiedades al objeto de la ventana . Mi primer uso de la palabra clave as en TypeScript.
AlanObject
33

Aunque el compilador se queja, aún debería generarlo como lo requiera. Sin embargo, esto funcionará.

var s = {};
s['prop'] = true;
Angelo R.
fuente
2
sí, bueno, esta no es realmente una forma de escritura tipo script, pierdes inteligencia.
pregmatch
26

Una opción más para hacer eso es acceder a la propiedad como una colección:

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

Aviw
fuente
2
Esta es la forma más concisa. Object.assign(obj, {prop: "value"})de ES6 / ES2015 también funciona.
Charles
9

Me sorprende que ninguna de las respuestas haga referencia a Object.assign, ya que esa es la técnica que uso cada vez que pienso en "composición" en JavaScript.

Y funciona como se esperaba en TypeScript:

interface IExisting {
    userName: string
}

interface INewStuff {
    email: string
}

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

const objectWithAllProps: IExisting & INewStuff = Object.assign({}, existingObject, {
    email: "[email protected]"
})

console.log(objectWithAllProps.email); // [email protected]

Ventajas

  • escriba seguridad en todo momento porque no necesita usar el anytipo en absoluto
  • utiliza el tipo agregado de TypeScript (como se indica &cuando declara el tipo de objectWithAllProps), lo que comunica claramente que estamos componiendo un nuevo tipo sobre la marcha (es decir, dinámicamente)

Cosas a tener en cuenta

  1. Object.assign tiene sus propios aspectos únicos (que son conocidos por los desarrolladores JS más experimentados) que deben tenerse en cuenta al escribir TypeScript.
    • Se puede usar de manera mutable o de manera inmutable (demuestro el modo inmutable anterior, lo que significa que existingObjectpermanece intacto y, por lo tanto, no tiene una emailpropiedad. Para la mayoría de los programadores de estilo funcional, eso es bueno ya que el resultado es El único cambio nuevo).
    • Object.assign funciona mejor cuando tienes objetos más planos. Si está combinando dos objetos anidados que contienen propiedades anulables, puede terminar sobrescribiendo valores verdaderos con indefinido. Si observa el orden de los argumentos de Object.assign, debería estar bien.
GreeneCreations
fuente
Para mí, en los casos en que necesita usar esto para mostrar datos, parece funcionar bien, pero cuando necesita agregar entradas de tipos modificados a una matriz, esto no parece funcionar demasiado bien. Recurrí a componer dinámicamente el objeto y asignarlo en una línea, luego asignar las propiedades dinámicas en líneas sucesivas. Esto me llevó casi todo el camino, así que gracias.
Shafiq Jetha
8

Puede crear un nuevo objeto basado en el objeto antiguo utilizando el operador de propagación

interface MyObject {
    prop1: string;
}

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

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

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

TypeScript inferirá todos los campos del objeto original y VSCode realizará el autocompletado, etc.

Alex
fuente
agradable, pero en caso de que necesite usar prop2 en, por ejemplo, prop3, será difícil de implementar
ya_dimon
6

Más simple será seguir

const obj = <any>{};
obj.prop1 = "value";
obj.prop2 = "another value"
Jayant Varshney
fuente
5

Es posible agregar un miembro a un objeto existente mediante

  1. ampliar el tipo (leer: ampliar / especializar la interfaz)
  2. lanzar el objeto original al tipo extendido
  3. agregar el miembro al objeto
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();
Daniel Dietrich
fuente
1
La mejor solución para mí. Aprendí algo nuevo hoy, gracias
Maurice
me sale el error TS2352 con este código
ya_dimon
@ya_dimon pruébalo en TypeScript Playground: typescriptlang.org/play ¡Funciona bien!
Daniel Dietrich
@DanielDietrich ahora funciona ... gracias, no tengo idea de lo que pasó.
ya_dimon
¡de nada! cambio ocurre;)
Daniel Dietrich
5

Como no puedes hacer esto:

obj.prop = 'value';

Si su compilador TS y su linter no son estrictos, puede escribir esto:

obj['prop'] = 'value';

Si su compilador o linter TS es estricto, otra respuesta sería escribir a máquina:

var obj = {};
obj = obj as unknown as { prop: string };
obj.prop = "value";
LEMUEL ADANE
fuente
3
Esto es si 'noImplicitAny: false' en tsconfig.json
LEMUEL ADANE
De lo contrario, puede hacer la respuesta GreeneCreations.
LEMUEL ADANE
4

Almacene cualquier propiedad nueva en cualquier tipo de objeto mediante la conversión de tipo a 'any':

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

Más tarde, puede recuperarlo volviendo su objeto extendido a 'any':

var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;
Erlend Robaye
fuente
Esta es totalmente la solución correcta. Digamos que tiene un objeto let o: ObjectType; .... más adelante puede lanzar o a cualquier (<any> o) .newProperty = 'foo'; y se puede recuperar como (<any> o) .newProperty. No hay errores de compilación y funciona de maravilla.
Matt Ashley
esto frena intellisense ... ¿hay alguna forma de esto, pero mantener intellisense?
pregmatch
4

Para garantizar que el tipo sea un Object(es decir , pares clave-valor ), use:

const obj: {[x: string]: any} = {}
obj.prop = 'cool beans'
bersling
fuente
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

ikarayel
fuente
4

puedes usar esto:

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

Puede agregar esta declaración para silenciar las advertencias.

declare var obj: any;

joshuapoehls
fuente
3

La mejor práctica es usar la escritura segura, te recomiendo:

interface customObject extends MyObject {
   newProp: string;
   newProp2: number;
}
Isidro Martínez
fuente
3

Para preservar su tipo anterior, transmita temporalmente su objeto a cualquier

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

La nueva propiedad dinámica solo estará disponible cuando use el elenco:

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

Aquí hay una versión especial de Object.assign, que ajusta automáticamente el tipo de variable con cada cambio de propiedad. No necesita variables adicionales, aserciones de tipo, tipos explícitos o copias de objetos:

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: La muestra hace uso de las funciones de aserción TS 3.7 . El tipo de retorno de assignes void, a diferencia de Object.assign.

ford04
fuente
1

Si está utilizando Typecript, presumiblemente desea utilizar el tipo de seguridad; en cuyo caso el Objeto desnudo y 'cualquiera' están contraindicados.

Es mejor no usar Object o {}, sino algún tipo con nombre; o puede estar utilizando una API con tipos específicos, que necesita ampliar con sus propios campos. He encontrado que esto funciona:

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";
Jack Punt
fuente
1

Asignar dinámicamente propiedades a un objeto en TypeScript.

para hacer eso solo necesita usar interfaces mecanografiadas de esta manera:

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

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

puedes usarlo así

var obj: IType = {};
obj['code1'] = { 
    prop1: 'prop 1 value', 
    prop2: 'prop 2 value' 
};
Nerdroid
fuente
1
Intento con su código, pero no recibo ningún error: pastebin.com/NBvJifzN
pablorsk
1
intenta inicializar el attributescampo dentro SomeClass y eso debería solucionarlo public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid
1

Prueba esto:

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

Entonces úsalo

const query = {
    name: 'abc'
}
query.page = 1
Tung Nguyen
fuente
0

La única solución que es totalmente segura para escribir es esta , pero es un poco prolija y te obliga a crear múltiples objetos.

Si primero debe crear un objeto vacío, luego elija una de estas dos soluciones. Tenga en cuenta que cada vez que usa as, está perdiendo seguridad.

Solución más segura

El tipo de objectes seguro en el interior getObject, lo que significa object.aque será de tipostring | undefined

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

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

Solución corta

El tipo de noobject es seguro en el interior getObject, lo que significa object.aque será de tipo stringincluso antes de su asignación.

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

function getObject() {
  const object = {} as Example;
  object.a = 'one';
  object.b = 1;
  return object;
}
fregante
fuente