868

If I wanted to programmatically assign a property to an object in Javascript, I would do it like this:

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

But in TypeScript, this generates an error:

The property 'prop' does not exist on value of type '{}'

How do I assign any new property to an object in TypeScript?

2
  • interface DynamicObject { [key: string]: any } const object:DynamicObject; object['key']='Test value' Commented Jul 28, 2022 at 13:47
  • You should use what they call declaration merging. Read about it. An example is in passport.js' index.d.ts
    – Gilbert
    Commented Nov 5, 2022 at 15:20

30 Answers 30

1142

Index signatures

It is possible to denote obj as any, but that defeats the whole purpose of using typescript. obj = {} implies obj is an Object. Marking it as any makes no sense. To accomplish the desired consistency an interface could be defined as follows, using an index signature

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

OR to make it compact:

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

LooseObject can accept fields with any string as key and any type as value.

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

The real elegance of this solution is that you can include typesafe fields in the 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;

Record<Keys,Type> utility type

Update (August 2020): @transang brought up the Record<Keys,Type> utility type in comments

Record<Keys,Type> is a Utility type in typescript. It is a much cleaner alternative for key-value pairs where property-names are not known. It's worth noting that Record<Keys,Type> is a named alias to {[k: Keys]: Type} where Keys and Type are generics. IMO, this makes it worth mentioning here

For comparison,

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

becomes

var obj: Record<string,any> = {}

MyType can now be defined by extending Record type

interface MyType extends Record<string,any> {
    typesafeProp1?: number,
    requiredProp1: string,
}

While this answers the Original question, the answer here by @GreeneCreations might give another perspective on how to approach the problem.

6
  • 27
    I think this is the best solution now. I think at the time the question was asked index properties like this were not yet implemented in TypeScript. Commented Jun 8, 2017 at 19:56
  • 1
    It does make sense when you have dynamic data. If you are receiving dynamic data from API, to build form, and then sending back dynamic values to api, it does make sense. Any is there for a reason, obviously you should strongly type most of stuff which is not dynamic, but in dynamic cases you can't strongly type.
    – sensei
    Commented Jun 13, 2019 at 15:03
  • 10
    now, you can write Record<string, any>, instead of {[key: string]: any}
    – Sang
    Commented Aug 20, 2020 at 11:00
  • 4
    It is now recommended to use Record<string, unknown> to avoid using the any type
    – Mathilda
    Commented Jan 13, 2022 at 6:37
  • why 'interface' instead of 'type' ?
    – mochsner
    Commented Nov 18, 2023 at 5:05
110

Or all in one go:

  var obj:any = {}
  obj.prop = 5;
4
  • 128
    What's the point of TypeScript if I have to cast so many things to any in order to use it? Just becomes extra noise in my code.. :/
    – Ozymandias
    Commented Jul 22, 2016 at 19:04
  • 15
    @AjaxLeung You should very rarely cast to any. TypeScript is used to catch (potential) errors at compile time. If you cast to any to mute errors then you lose the power of typing and may as well go back to pure JS. any should ideally only be used if you're importing code for which you cannot write TS definitions or whilst migrating your code from JS to TS
    – Precastic
    Commented Jan 19, 2017 at 6:14
  • No matter what the best practice or purpose of typescript is, this is the right answer to the question.
    – Sam
    Commented Jan 26, 2023 at 20:19
  • 4
    @M.Arjmandi - it's really not. The question is asking how you should approach this in terms of typescript. The whole point of typescript is to avoid the annoying side of javascript being dynamic - everything in javascript is effectively cast to any be default. It's objectively not a good answer.
    – kmars
    Commented Mar 1, 2023 at 6:11
108

This solution is useful when your object has Specific Type. Like when obtaining the object to other source.

let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
2
  • 15
    This is the correct solution for one-off assignments. Commented Jan 30, 2017 at 6:12
  • 2
    This answer worked for me because for facebook login I had to add properties to the window object. My first use of the as keyword in TypeScript.
    – AlanObject
    Commented Mar 9, 2019 at 1:17
88

I tend to put any on the other side i.e. var foo:IFoo = <any>{}; So something like this is still typesafe:

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; 

Alternatively you can mark these properties as optional:

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

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

Try it online

6
  • 5
    +1 for being the only solution that keeps type safety. Just make sure you instanciate all non-optional properties directly after it, to avoid bugs biting you later on.
    – Aidiakapi
    Commented Apr 8, 2014 at 15:18
  • Does this actually work? After compiling I still have <any>{} in my javascript.
    – bvs
    Commented Jan 22, 2015 at 2:10
  • 4
    I still have <any>{ then you haven't compiled. TypeScript would remove that in its emit
    – basarat
    Commented Jan 22, 2015 at 3:06
  • 8
    These days, var foo: IFoo = {} as any is preferred. The old syntax for typecasting was colliding with TSX (Typescript-ified JSX).
    – Don
    Commented Dec 17, 2018 at 20:45
  • It's a bit confusing to me, how does<any>{} respect the type of IFoo?
    – user12582392
    Commented Dec 29, 2021 at 19:08
52

Although the compiler complains it should still output it as you require. However, this will work.

const s = {};
s['prop'] = true;
4
  • 14
    yes well, this is not really type script way, you loose intellisense.
    – pregmatch
    Commented Nov 14, 2018 at 18:54
  • Works nice for object with dynamic structure (cannot be defined in an interface)
    – Camille
    Commented Nov 25, 2020 at 8:21
  • 4
    do not use var.. instead use let or const
    – GorvGoyl
    Commented May 22, 2021 at 13:37
  • "dynamically".... Commented Sep 19, 2023 at 13:56
51

I'm surprised that none of the answers reference Object.assign since that's the technique I use whenever I think about "composition" in JavaScript.

And it works as expected in 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]

Advantages

  • type safety throughout because you don't need to use the any type at all
  • uses TypeScript's aggregate type (as denoted by the & when declaring the type of objectWithAllProps), which clearly communicates that we're composing a new type on-the-fly (i.e. dynamically)

Things to be aware of

  1. Object.assign has it's own unique aspects (that are well known to most experienced JS devs) that should be considered when writing TypeScript.
    • It can be used in a mutable fashion, or an immutable manner (I demonstrate the immutable way above, which means that existingObject stays untouched and therefore doesn't have an email property. For most functional-style programmers, that's a good thing since the result is the only new change).
    • Object.assign works the best when you have flatter objects. If you are combining two nested objects that contain nullable properties, you can end up overwriting truthy values with undefined. If you watch out for the order of the Object.assign arguments, you should be fine.
2
  • For me, in instances where you need to use this to display data it seems to work fine, but when you need to add entries of modified types to an array, this doesn't seem to work too well. I resorted to dynamically composing the object and assigning it in one line, then assigning the dynamic properties in successive lines. This got me most of the way there, so thank you. Commented Apr 21, 2020 at 13:34
  • 1
    I tried this with an Express Request object and discovered that the "immutable" method Object.assign( {}, req, myObj ) will break the Request object in unexpected ways, such as causing headers to become undefined. However, as suggested above the "mutation" method worked great: Object.assign( req, myObj ).
    – Max Wilder
    Commented Mar 12, 2023 at 20:49
27

One more option do to that is to access the property as a collection:

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

1
  • 7
    This is the most concise way. Object.assign(obj, {prop: "value"}) from ES6/ES2015 works, too.
    – C.W.
    Commented Mar 28, 2018 at 23:58
23

You can create new object based on the old object using the spread operator

interface MyObject {
    prop1: string;
}

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

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

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

TypeScript will infer all the fields of the original object and VSCode will do autocompletion, etc.

2
  • nice, but in case you need to use prop2 in e.g. prop3, will be hard to implement
    – ya_dimon
    Commented Jun 9, 2020 at 15:56
  • 1
    Not sure if I follow the statement - there is no prop3 in this example.
    – Alex
    Commented Aug 31, 2020 at 8:42
23

Late but, simple answer

let prop = 'name';
let value = 'sampath';
this.obj = {
   ...this.obj,
   [prop]: value
};
13

you can use this :

this.model = Object.assign(this.model, { newProp: 0 });
2
  • 4
    You don't need this.model = .
    – wortwart
    Commented Aug 25, 2020 at 12:16
  • this.model = { ...this.model, { newProp: 0 }}; Commented Dec 8, 2021 at 2:13
12

Here is a special version of Object.assign, that automatically adjusts the variable type with every property change. No need for additional variables, type assertions, explicit types or object copies:

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 }

Note: The sample makes use of TS 3.7 assertion functions. The return type of assign is void, unlike Object.assign.

1
  • This assert way also works to mix-in any arbitrary property into an existing object/class. eg. function addProperty<T extends Record<string, any>, K extends string>(toObj: T, key: K, value: any): assert T is T & { [K]: any } { toObj[key] = value; }. I had it returning a new object with the property mixed-in, but that didn't tell TS that the original input object actually got mutated (I had to declare a new interface with the mixed-in property(ies) before TS would recognize them). assert as the return type takes care of that, with no extra interface defs required. Thanks! Commented Jan 16 at 15:57
12

Case 1:

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

Case 2:

var car:any = {type: "BMW", model: "i8", color: "white"};
car.owner = "ibrahim"; // You can set a property: use any type
1
  • If you use any this way, you might as well not use TypeScript. Don’t use any if there are other solutions.
    – fregante
    Commented Feb 4 at 11:23
10

Simplest will be following

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

Since you cannot do this:

obj.prop = 'value';

If your TS compiler and your linter does not strict you, you can write this:

obj['prop'] = 'value';

If your TS compiler or linter is strict, another answer would be to typecast:

var obj = {};
obj = obj as unknown as { prop: string };
obj.prop = "value";
2
  • 5
    This is if 'noImplicitAny: false' on the tsconfig.json Commented Apr 3, 2019 at 9:35
  • Else you can do GreeneCreations answer. Commented Apr 3, 2019 at 9:36
7

To guarantee that the type is an Object (i.e. key-value pairs), use:

const obj: {[x: string]: any} = {}
obj.prop = 'cool beans'
1
  • This solution worked for me because without the additional type information I was still getting this error: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. Commented Mar 25, 2021 at 12:18
6

It is possible to add a member to an existing object by

  1. widening the type (read: extend/specialize the interface)
  2. cast the original object to the extended type
  3. add the member to the object
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();
0
4

The best practice is use safe typing, I recommend you:

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

The only solution that is fully type-safe is this one, but is a little wordy and forces you to create multiple objects.

If you must create an empty object first, then pick one of these two solutions. Keep in mind that every time you use as, you're losing safety.

Safer solution

The type of object is safe inside getObject, which means object.a will be of type string | undefined

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

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

Short solution

The type of object is not safe inside getObject, which means object.a will be of type string even before its assignment.

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

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

Extending @jmvtrinidad solution for Angular,

When working with a already existing typed object, this is how to add new property.

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

Now if you want to use otherProperty in html side, this is what you'd need:

<div *ngIf="$any(user).otherProperty">
   ...
   ...
</div>

Angular compiler treats $any() as a cast to the any type just like in TypeScript when a <any> or as any cast is used.

4

Simply do this, and you can add or use any property. (I am using typescript version as "typescript": "~4.5.5")

let contextItem = {} as any;

Now, you can add any property and use it any where. like

contextItem.studentName = "kushal";

later you can use it as:

console.log(contextItem.studentName);
3

Store any new property on any kind of object by typecasting it to 'any':

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

Later on you can retrieve it by casting your extended object back to 'any':

var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;
2
  • This is totally the correct solution. Lets say you have an object let o : ObjectType; .... later on you can cast o to any (<any>o).newProperty = 'foo'; and it can be retrieve like (<any>o).newProperty. No compiler errors and works like a charm. Commented Jul 7, 2016 at 16:24
  • this brakes intellisense ... is there any way to this this but to keep intellisense?
    – pregmatch
    Commented Nov 14, 2018 at 18:54
3

To preserve your previous type, temporary cast your object to any

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

The new dynamic property will only be available when you use the cast:

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

dynamically assign properties to an object in TypeScript.

to do that You just need to use typescript interfaces like so:

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

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

you can use it like that

var obj: IType = {};
obj['code1'] = { 
    prop1: 'prop 1 value', 
    prop2: 'prop 2 value' 
};
2
  • 1
    I try with your code, but I don't receive any error: pastebin.com/NBvJifzN
    – pablorsk
    Commented Apr 3, 2017 at 12:08
  • 1
    try to initialise the attributes field inside SomeClass and that should fix it public attributes: IType = {}; pastebin.com/3xnu0TnN
    – Nerdroid
    Commented Apr 3, 2017 at 23:47
2

If you don't want to use any, you can use do this:

type Obj = Record<string, unknown>

var obj: Obj = {};
obj.prop = "value";
obj.prop2 = 1
1

I wrote an article tackling this very topic:

Typescript – Enhance an object and its type at runtime

https://tech.xriba.io/2022/03/24/typescript-enhance-an-object-and-its-type-at-runtime/

Maybe you could take inspiration from Typescript concepts such:

0
1

If you are using Typescript, presumably you want to use the type safety; in which case naked Object and 'any' are counterindicated.

Better to not use Object or {}, but some named type; or you might be using an API with specific types, which you need extend with your own fields. I've found this to work:

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 = getTheGivenObject() as PropAble;
k.props = "value for props";
0

I ran into this problem when trying to do a partial update of an object that was acting as a storage for state.

type State = {
  foo: string;
  bar: string;
  baz: string;
};

const newState = { foo: 'abc' };

if (someCondition) {
  newState.bar = 'xyz'
}

setState(newState);

In this scenario, the best solution would be to use Partial<T>. It makes all properties on the provided type optional using the ? token. Read more about it in a more specific SO topic about making all properties on a type optional.

Here's how I solved it with Partial<T>:

type State = {
  foo: string;
  bar: string;
  baz: string;
};

const newState: Partial<State> = { foo: 'abc' };

if (someCondition) {
  newState.bar = 'xyz';
}

setState(newState);

This is similar to what fregante described in their answer, but I wanted to paint a clearer picture for this specific use case (which is common in frontend applications).

0

Use ES6 Map whenever a map can take truly arbitrary values of fixed type, and optional properties otherwise

I think this is the guideline I'll go for. ES6 map can be done in typescript as mentioned at: ES6 Map in Typescript

The main use case for optional properties are "options" parameters of functions: Using named parameters JavaScript (based on typescript) In that case, we do know in advance the exact list of allowed properties, so the sanest thing to do is to just define an explicit interface, and just make anything that is optional optional with ? as mentioned at: https://stackoverflow.com/a/18444150/895245 to get as much type checking as possible:

const assert = require('assert')

interface myfuncOpts {
  myInt: number,
  myString?: string,
}

function myfunc({
  myInt,
  myString,
}: myfuncOpts) {
  return `${myInt} ${myString}`
}

const opts: myfuncOpts = { myInt: 1 }
if (process.argv.length > 2) {
  opts.myString = 'abc'
}

assert.strictEqual(
  myfunc(opts),
  '1 abc'
)

And then I'll use Map when it is something that is truly arbitrary (infinitely many possible keys) and of fixed type, e.g.:

const assert = require('assert')
const integerNames = new Map<number, string>([[1, 'one']])
integerNames.set(2, 'two')
assert.strictEqual(integerNames.get(1), 'one')
assert.strictEqual(integerNames.get(2), 'two')

Tested on:

  "dependencies": {
    "@types/node": "^16.11.13",
    "typescript": "^4.5.4"
  }
-1

Try this:

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

Then use it

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

You can add this declaration to silence the warnings.

declare var obj: any;
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.