TypeScript
Dynamic Programming
Object Properties
Web Development
JavaScript

How do I dynamically assign properties to an object in TypeScript?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In TypeScript, dynamically assigning properties is easy in JavaScript terms and constrained in type-system terms. The main question is not “Can I add a property at runtime?” because you can. The real question is how much static type safety you want while doing it.

The Simplest Option: an Index Signature

If the object really is a dynamic key-value bag, give it an index signature.

typescript
1interface StringMap {
2  [key: string]: string;
3}
4
5const userMeta: StringMap = {};
6userMeta["role"] = "admin";
7userMeta["theme"] = "dark";
8
9console.log(userMeta);

This tells TypeScript that any string key is allowed and that every value must be a string.

That is usually the best answer when the dynamic nature is part of the design rather than a temporary workaround.

Use Record for a Similar Pattern

Record is a utility type that expresses the same idea more compactly.

typescript
1const counts: Record<string, number> = {};
2counts["apples"] = 3;
3counts["oranges"] = 5;
4
5console.log(counts);

Record<string, number> is often easier to read than a custom interface when the type is simple.

Add Properties to Objects with Known Fields

Sometimes the object has a few required properties and also allows additional ones. In that case, combine fixed properties with an index signature.

typescript
1interface UserConfig {
2  id: string;
3  [key: string]: string | number;
4}
5
6const config: UserConfig = { id: "u-1" };
7config["theme"] = "dark";
8config["retryCount"] = 3;
9
10console.log(config);

This keeps the known fields typed while still allowing extra dynamic properties.

Use any Only When You Truly Want No Checking

You can always disable type safety by using any.

typescript
1const obj: any = {};
2obj.foo = "bar";
3obj.answer = 42;
4obj.enabled = true;

This works, but it throws away the main benefit of TypeScript. It should usually be the last resort, not the first choice.

Constrain Dynamic Keys with a Union

If the set of allowed keys is dynamic at runtime but still known at design time, use a key union rather than a fully open index signature.

typescript
1type SettingKey = "theme" | "language" | "timezone";
2
3type Settings = Partial<Record<SettingKey, string>>;
4
5const settings: Settings = {};
6settings.theme = "dark";
7settings.language = "en";
8
9console.log(settings);

This is safer because TypeScript rejects unsupported keys.

Build Objects Incrementally

When object properties are assembled in steps, Partial<T> is often useful.

typescript
1interface User {
2  id: string;
3  name: string;
4  email: string;
5}
6
7const draft: Partial<User> = {};
8draft.id = "u-1";
9draft.name = "Ava";
10draft.email = "[email protected]";
11
12const user = draft as User;
13console.log(user);

This pattern is convenient, but the final cast should be used carefully. It assumes you really did populate all required fields.

Computed Property Names Still Need Compatible Types

Dynamic property names also work with normal objects as long as the type allows them.

typescript
1const key = "status";
2const result: Record<string, string> = {};
3result[key] = "active";
4
5console.log(result);

The runtime behavior is ordinary JavaScript. The only TypeScript requirement is that the object's type admits that key/value pattern.

Pick the Type to Match the Real Shape

A good rule is:

  • use fixed interfaces when the object shape is truly known
  • use Record or an index signature for key-value maps
  • use Partial<T> for stepwise construction
  • avoid any unless the data is genuinely untyped and temporary

The cleaner the type matches the real object behavior, the fewer assertions and workarounds you need later.

Common Pitfalls

A common mistake is creating {} and then assigning arbitrary properties without giving the object a type that allows those keys. TypeScript then correctly reports an error.

Another mistake is using any for convenience and losing all type checking across the rest of the code path.

Developers also often use an open Record<string, any> when the keys are actually known and could have been modeled more safely with a union or interface.

Finally, Partial<T> is helpful for incremental construction, but it does not guarantee all required fields are eventually present unless you validate before final use.

Summary

  • Dynamic property assignment is allowed in TypeScript when the type says it is allowed.
  • Use an index signature or Record for true key-value maps.
  • Combine fixed fields with an index signature when the object has both known and dynamic properties.
  • Use Partial<T> for staged object construction.
  • Prefer the most specific safe type instead of reaching for any immediately.

Course illustration
Course illustration

All Rights Reserved.