How do I dynamically assign properties to an object in TypeScript?
If I wanted to programatically 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 am I supposed to assign any new property to an object in TypeScript?
Index types
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.
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 this up 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 thatRecord<Keys,Type>
is a named alias to{[k: Keys]: Type}
whereKeys
andType
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.
Or all in one go:
var obj:any = {}
obj.prop = 5;
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.
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