Como atribuir propriedades dinamicamente a um objeto no TypeScript?


359

Se eu quisesse atribuir programaticamente uma propriedade a um objeto em Javascript, faria assim:

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

Mas no TypeScript, isso gera um erro:

A propriedade 'prop' não existe no valor do tipo '{}'

Como devo atribuir qualquer nova propriedade a um objeto no TypeScript?


11
PARA SUA INFORMAÇÃO. Eu abriu uma discussão relacionada a este se você gostaria de segui-lo.
Joshuapoehls

Respostas:


456

É possível denotar objcomo any, mas isso anula todo o objetivo do uso de texto datilografado. obj = {}implica objé um Object. Marcá-lo como anynão faz sentido. Para obter a consistência desejada, uma interface pode ser definida da seguinte maneira.

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

OU para torná-lo compacto:

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

LooseObjectpode aceitar campos com qualquer sequência como chave e anydigitar como valor.

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

A verdadeira elegância dessa solução é que você pode incluir campos de segurança tipográfica na interface.

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;

Enquanto isso responde à pergunta original, a resposta aqui de @GreeneCreations pode dar outra perspectiva de como abordar o problema.


13
Eu acho que essa é a melhor solução agora. Acho que no momento em que a pergunta foi feita, as propriedades de índice como essa ainda não foram implementadas no TypeScript.
27668 Peter

como sobre o objeto nativo como Erro
Wayou

Objetos nativos do @Wayou, como Error e Array, herdam de Object. Então, LooseObjectpode levar um erro também.
Akash Kurian Jose

Você pode verificar se o objeto tem uma determinada propriedade (dinâmica)?
Phhbr 3/0318

11
para esses objetos soltos, como você os escreve em forma de getter-setter?
JokingBatman

80

Ou tudo de uma só vez:

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

42
Qual é o sentido do TypeScript se eu precisar converter tantas coisas anypara usá-lo? Apenas se torna ruído extra no meu código ..: /
AjaxLeung

16
@AjaxLeung Se você precisar fazer isso, está usando errado.
Alex

2
Veja a edição adicional acima que preserva o tipo anterior do objeto.
Andre Vianna

6
@AjaxLeung Você raramente deve usar o elenco any. TypeScript é usado para capturar erros (potenciais) em tempo de compilação. Se você converter para anysilenciar erros, perderá o poder de digitar e poderá voltar ao JS puro. anyidealmente, só deve ser usado se você estiver importando código para o qual não pode escrever definições de TS ou enquanto estiver migrando seu código de JS para TS
Precastic

63

Eu costumo colocar anydo outro lado, ou seja, var foo:IFoo = <any>{};algo assim ainda é 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; 

Como alternativa, você pode marcar essas propriedades como opcionais:

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

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

Experimente online


5
+1 por ser a única solução que mantém a segurança do tipo. Apenas certifique-se de instanciar todas as propriedades não opcionais diretamente após ela, para evitar erros que o atrapalhem mais tarde.
Aidiakapi

Isso realmente funciona? Depois de compilar, ainda tenho <any> {} no meu javascript.
bvs

4
I still have <any>{então você não compilou. Typescript removeria que em sua Emit
basarat

4
Hoje em dia, var foo: IFoo = {} as anyé o preferido. A sintaxe antiga para a conversão de tipos estava colidindo com o TSX (JSX datilografado).
Don

51

Esta solução é útil quando seu objeto possui um tipo específico. Como ao obter o objeto para outra fonte.

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

7
Esta é a solução correta para tarefas pontuais.
soundly_typed

Essa resposta funcionou para mim porque, para o login no facebook, tive que adicionar propriedades ao objeto da janela . Meu primeiro uso do como palavra-chave à máquina.
precisa saber é o seguinte

33

Embora o compilador reclame, ele ainda deve gerá-lo conforme necessário. No entanto, isso vai funcionar.

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

2
Sim, bem, esse não é realmente o tipo de script, você perde o senso comum.
Pregmatch

26

Mais uma opção a fazer é acessar a propriedade como uma coleção:

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


2
Esta é a maneira mais concisa. Object.assign(obj, {prop: "value"})do ES6 / ES2015 também funciona.
Charles Charles

9

Estou surpreso que nenhuma das respostas faça referência a Object.assign, pois essa é a técnica que uso sempre que penso em "composição" em JavaScript.

E funciona como esperado no 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

Vantagens

  • digite segurança por toda parte, porque você não precisa usar o anytipo
  • usa o tipo agregado do TypeScript (como indicado pelo &ao declarar o tipo de objectWithAllProps), que comunica claramente que estamos compondo um novo tipo on-the-fly (ou seja, dinamicamente)

Coisas para estar ciente

  1. O Object.assign possui seus próprios aspectos exclusivos (que são bem conhecidos pelos desenvolvedores JS mais experientes) que devem ser considerados ao escrever o TypeScript.
    • Ele pode ser usado de maneira mutável ou imutável (eu demonstro a maneira imutável acima, o que significa que existingObjectpermanece intocado e, portanto, não possui uma emailpropriedade. Para a maioria dos programadores de estilo funcional, isso é bom, pois o resultado é a única nova mudança).
    • Object.assign funciona melhor quando você tem objetos mais planos. Se você estiver combinando dois objetos aninhados que contêm propriedades anuláveis, poderá substituir os valores de verdade por indefinidos. Se você observar a ordem dos argumentos Object.assign, deverá ficar bem.

Para mim, nos casos em que você precisa usar isso para exibir dados, parece funcionar bem, mas quando você precisa adicionar entradas de tipos modificados a uma matriz, isso parece não funcionar muito bem. Eu comecei a compor dinamicamente o objeto e atribuí-lo em uma linha, depois a atribuir as propriedades dinâmicas em linhas sucessivas. Isso me levou a maior parte do caminho até lá, então obrigado.
Shafiq Jetha 21/04

8

Você pode criar um novo objeto com base no objeto antigo usando o operador de propagação

interface MyObject {
    prop1: string;
}

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

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

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

O TypeScript inferirá todos os campos do objeto original e o VSCode executará o preenchimento automático, etc.


bom, mas no caso de você precisar usar prop2 em, por exemplo, prop3, será difícil de implementar
ya_dimon

6

Mais simples seguirá

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

5

É possível adicionar um membro a um objeto existente,

  1. ampliando o tipo (leia: estenda / especialize a interface)
  2. converter o objeto original no tipo estendido
  3. adicione o membro ao 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();

11
a melhor solução para mim. Eu aprendi algo novo hoje, obrigado
Maurice

eu recebo o erro TS2352 com este código
ya_dimon

@ya_dimon tente no Playground TypeScript: typescriptlang.org/play Funciona bem!
Daniel Dietrich

@DanielDietrich agora funciona .. thx, não faço ideia do que aconteceu.
ya_dimon 10/06

de nada! mudança acontece;)
Daniel Dietrich

5

Como você não pode fazer isso:

obj.prop = 'value';

Se o seu compilador TS e seu linter não o restringem, você pode escrever o seguinte:

obj['prop'] = 'value';

Se o seu compilador ou ponteiro TS for rigoroso, outra resposta seria digitar:

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

3
Isto é se 'noImplicitAny: false' no tsconfig.json
LEMUEL ADANE 03/04/19

Caso contrário, você pode responder ao GreeneCreations.
LEMUEL Adane

4

Armazene qualquer nova propriedade em qualquer tipo de objeto digitando-a como 'any':

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

Posteriormente, você pode recuperá-lo lançando seu objeto estendido de volta para 'any':

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

Esta é totalmente a solução correta. Digamos que você tenha um objeto, deixe o: ObjectType; .... posteriormente, você pode converter o para qualquer (<qualquer> o) .newProperty = 'foo'; e pode ser recuperado como (<any> o) .newProperty. Sem erros de compilação e funciona como um encanto.
Matt Ashley

isso freia o senso comum ... existe alguma maneira de fazer isso, senão manter o senso comum?
Pregmatch

4

Para garantir que o tipo seja um Object(ou seja , pares de valores-chave ), use:

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

você pode usar isso:

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

3

Você pode adicionar esta declaração para silenciar os avisos.

declare var obj: any;


3

A melhor prática é usar a digitação segura, eu recomendo que você:

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

3

Para preservar seu tipo anterior, projete temporariamente seu objeto em qualquer

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

A nova propriedade dinâmica estará disponível apenas quando você usar o elenco:

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

3

Aqui está uma versão especial de Object.assign, que ajusta automaticamente o tipo de variável a cada alteração de propriedade. Não há necessidade de variáveis ​​adicionais, asserções de tipo, tipos explícitos ou cópias 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: A amostra utiliza as funções de asserção TS 3.7 . O tipo de retorno de assigné voiddiferente Object.assign.


1

Se você estiver usando o Typecript, provavelmente você deseja usar o tipo safety; nesse caso, Objeto nu e 'qualquer' são contra-indicados.

Melhor não usar Object ou {}, mas algum tipo nomeado; ou você pode estar usando uma API com tipos específicos, que você precisa estender com seus próprios campos. Descobri que isso 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";

1

atribuir dinamicamente propriedades a um objeto no TypeScript.

para fazer isso Você só precisa usar interfaces de texto datilografado da seguinte maneira:

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

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

você pode usá-lo assim

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

11
Eu tento com o seu código, mas eu não receber qualquer erro: pastebin.com/NBvJifzN
pablorsk

11
tente inicializar o attributescampo dentro SomeClass e isso deve corrigi-lo public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid 3/17/17

1

Tente o seguinte:

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

Então use

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

0

A única solução totalmente segura para o tipo é essa , mas é um pouco prolixo e obriga a criar vários objetos.

Se você deve criar um objeto vazio primeiro, escolha uma dessas duas soluções. Lembre-se de que toda vez que você usa as, perde a segurança.

Solução mais segura

O tipo de objecté seguro por dentro getObject, o que significa que object.aserá do tipostring | undefined

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

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

Solução curta

O tipo de nãoobject é seguro por dentro getObject, o que significa que object.aserá do tipo stringmesmo antes de sua atribuição.

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

function getObject() {
  const object = {} as Example;
  object.a = 'one';
  object.b = 1;
  return object;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.