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.

Answer from Akash on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org › typescript › how-to-create-objects-with-dynamic-keys-in-typescript
How to Create Objects with Dynamic Keys in TypeScript ? - GeeksforGeeks
July 23, 2025 - We can leverage mapped types to dynamically generate object types with specific keys and value types. type DynamicObject<KeyType extends string | number, ValueType> = { [K in KeyType]: ValueType; }; Example...
🌐
Medium
medium.com › @jofaval › objecy-with-dynamic-keys-in-typescript-468c358a5f8b
Objects with Dynamic Keys in TypeScript | by Pepe Fabra Valverde | Medium
October 20, 2023 - A short little article about TypeScript’s inference system and how we can pre-define custom keys, dynamically · This may have duplicates, there may be articles already written about this. But, at the time when I needed this, I was not able to find something similar, not quite what I had in mind. const useVisible = <TName extends string>(name: TName) => { const [visible, setVisibility] = useState(false); const capitalized = (name.charAt(0).toLocaleUpperCase() + name.substring(1)) as Capitalize<TName>; type TCap = typeof capitalized; type VisibleReturn = { [k in `close${TCap}`]: () => void; } & { [k in `is${TCap}Visible`]: boolean; } & { [k in `open${TCap}`]: () => void; }; return { [`close${capitalized}`]: () => setVisibility(false), [`is${capitalized}Visible`]: visible, [`open${capitalized}`]: () => setVisibility(true), } as { [k in keyof VisibleReturn]: VisibleReturn[k]; }; };
Discussions

Dynamic object keys in TypeScript
I have a problem when setting the type of a dynamic object in TypeScript because the object that i create has dynamic keys and three that are not. Here’s how i defined the type for the object: interface GraphReturns { … More on forum.freecodecamp.org
🌐 forum.freecodecamp.org
2
0
June 10, 2022
node.js - How to dynamically set an object key in typescript - Stack Overflow
I just fixed a syntax error (made object of type any and removed the var before object[key] = ...), it should be typescript-compliant now. More on stackoverflow.com
🌐 stackoverflow.com
How to set an an object with a dynamic key as React state in Typescript
How can I effectively define an ... state in Typescript? ... What do you mean by dynamic keys. Do you mean what an object which can accept every string as key. Or an object which accepts any key from specific set of strings? Please explain a little more. ... I think it would be more understandable if you gave an example of what you ... More on stackoverflow.com
🌐 stackoverflow.com
Can I dynamically set the key of object using typescript? - Stack Overflow
I am looking for a way to dynamically type some values and methods on a class I have. Example I'll start simple. This is the behaviour I want. (I think) const options = ["opt1", "opt... More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 16
1174

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.

2 of 16
120

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.
🌐
Total TypeScript
totaltypescript.com › tutorials › beginners-typescript › beginner-s-typescript-section › assigning-dynamic-keys-to-an-object › solution
Techniques for Typing Dynamic Object Keys | Total TypeScript
June 7, 2023 - This type here, what that's describing is, basically, it allows us to add any number of dynamic keys to that object at runtime. That's what a record type is. 0:48 This is different from a set and a map that we saw in the previous exercise, because it's just at the type level.
🌐
Squash
squash.io › tutorial-working-with-dynamic-object-keys-in-typescript
Tutorial: Working with Dynamic Object Keys in TypeScript
May 7, 2024 - In this example, we define a variable dynamicKey with the value 'age'. We then create an object person using the bracket notation to assign the value 25 to the age key. The resulting object will have the properties name and age.
🌐
LogRocket
blog.logrocket.com › home › how to dynamically assign properties to an object in typescript
How to dynamically assign properties to an object in TypeScript - LogRocket Blog
October 15, 2024 - We can perform a type assertion by either using the <> brackets or the as keyword. This is particularly helpful with the dynamic property assignment because it allows the properties we want for our object to be dynamically set.
🌐
Omarileon
omarileon.me › blog › typescript-dynamic-object-keys
mari. | How to Create Objects with Dynamic Keys in TypeScript
April 15, 2024 - So to sum it up, you can put together an object that uses dynamic keys with the Record type, which takes a type parameter for the keys of your object, and a parameter for the possible values. If you’re looking for a generic object, chances are you want this type: ... I wouldn’t recommend using this because of the any, but TypeScript will let you do whatever you want with it:
Find elsewhere
🌐
freeCodeCamp
forum.freecodecamp.org › t › dynamic-object-keys-in-typescript › 516302
Dynamic object keys in TypeScript - The freeCodeCamp Forum
June 10, 2022 - Here’s how i defined the type for the object: interface GraphReturns { [key: string]: { '%': number, value: number, '�umulated': number }, total: number, 'total_%': number, date: string } The errors i get: Property ‘total’ of type ...
🌐
Total TypeScript
totaltypescript.com › tutorials › beginners-typescript › beginner-s-typescript-section › assigning-dynamic-keys-to-an-object
Assigning Dynamic Keys to an Object | Total TypeScript
June 7, 2023 - Seeing “index” in a type error message usually refers to the key of an object. In this lesson are a few techniques for properly typing dynamic object keys.
🌐
Webdevtutor
webdevtutor.net › blog › typescript-set-object-key-dynamically
Dynamically Setting Object Keys in TypeScript: A Comprehensive Guide
In conclusion, dynamically setting object keys in TypeScript can be achieved through various methods, including template literals, bracket notation, and Object.assign().
🌐
Squash
squash.io › tutorial-getting-object-value-by-dynamic-key-in-typescript
How to Get an Object Value by Dynamic Keys in TypeScript
October 14, 2023 - Let's consider an example where we have a Map object representing key-value pairs: const map = new Map&lt;string, string&gt;(); map.set("key", "value"); const dynamicKey = "key"; const value = map.get(dynamicKey); console.log(value); // Output: value
🌐
DEV Community
dev.to › logrocket › how-to-dynamically-assign-properties-to-an-object-in-typescript-58fg
How to dynamically assign properties to an object in TypeScript - DEV Community
September 13, 2022 - Apart from primitives, the most ... to build an object dynamically, take advantage of the Record utility type or use the object index signature to define the allowed properties on the object....
🌐
Sean C Davis
seancdavis.com › posts › mapping-dynamic-object-keys-in-typescript
Mapping Dynamic Object Keys in TypeScript | Sean C Davis
But it’s a tricky scenario to get right with TypeScript. Consider the example from the dynamic property map post: const buttonClassMap = { dark: "bg-black text-white", light: "bg-gray text-black", }; const theme = "light"; buttonClassMap[Object.keys(buttonClassMap).includes(theme) ?
🌐
How to
sharooq.com › how-to-use-dynamic-keys-with-typescript-objects
How to - use dynamic keys with TypeScript objects - Sharooq
April 18, 2023 - By using index signatures, the Record utility type, mapped types, and nested types, you can define and manipulate objects with dynamic keys efficiently and safely.
🌐
Upmostly
upmostly.com › home › typescript › how to create objects with dynamic keys
How to Create Objects with Dynamic Keys in TypeScript - Upmostly
October 13, 2023 - So to sum it up, you can put together an object that uses dynamic keys with the Record type, which takes a type parameter for the keys of your object, and a parameter for the possible values. If you’re looking for a generic object, chances are you want this type: ... I wouldn’t recommend using this because of the any, but TypeScript will let you do whatever you want with it:
Top answer
1 of 1
1

TypeScript doesn't really track type mutations very well; the default stance of the language is that an expression's type represents the possible values that expression can have, and that it does not change over time. So a variable let x: string = "foo" can never hold a number value, and if you think you might want it to, you should have annotated it like let x: string | number = "foo" instead.

Well, there is the concept of type narrowing, where the compiler will take a variable of type X and temporarily treats it as some subtype Y extends X based on control flow analysis. So let x: string | number = "foo" will cause the compiler to see x as narrowed to string, and you can write x.toUppercase() without error. If you reassign x = 4 the compiler will re-widen to string | number and then re-narrow to number so you can then write x.toFixed(2) without error:

let x: string | number = "foo";
x.toUpperCase(); // okay
x.toFixed // error
x = 5; // okay
x.toFixed(); // okay
x.toUpperCase // error

And when I first looked at your question I had some vague hope that maybe we could refactor your code so that it viewed what you were doing as this sort of scope-based type narrowing. There's even some functionality called assertion functions/methods where you annotate a void-returning class method as returning an assertion predicate of the form asserts this is Y where Y extends this, and the compiler knows that calling that function will narrow the type of the class instance.

But in practice it's very hard to use assertion methods, since they require explicit type annotations in places where you wouldn't think to use them (see microsoft/TypeScript#45385 for a feature request to lift this condition), and there's no easy way to use them to re-widen a narrowed type. And in this specific case I wasn't able to get the compiler to see the operation of calling setEquation() on an Activity as a narrowing (due to vagaries of how the compiler measures variance), so calling it as an assertion method had no effect.

The point is, this sort of thing is hard to do.


So instead of trying to do this as a mutation, we can reframe this as having the operations producing results of new types that you store in new variables if you need to refer to them multiple times. This is like using a fluent interface where you chain methods together. The rule here is that you never re-use a variable if you have performed an operation on it which would mutate its state. (You could guard against that by making everything immutable, so activity.setEquation() would return a brand new Activity instead of mutating the existing one, but I'm not going to worry about that now).

For example:

type EquationParams<K extends string> = {
    variables: K[];
};

class Equation<K extends string> {
    id: string;
    equation?: string;
    variables: K[] = [];

    constructor(params: EquationParams<K>) {
        this.id = "test";
        this.variables = params.variables;
    }

    public addVariable<L extends string>(variables: L[] | L): Equation<K | L> {
        const deDupe = new Set<K | L>(this.variables);

        if (variables instanceof Array) {
            variables.forEach((variable) => deDupe.add(variable));
        } else {
            deDupe.add(variables);
        }
        const that = this as Equation<K | L>;
        that.variables = Array.from(deDupe);
        return that;
    }
}

Here, Equation is now generic in the string literal types of the elements of its variables property. If you have an Equation<K> and call addVariable(v) where v is of type L, the compiler returns an Equation<K | L>, using a union type to represent an array of values of type K together with those of type L. Note that we need at least one type assertion in the implementation (const that = this as Equation<K | L>) to convince the compiler to start adding new L variables that are not in Equation<K>.

For completeness, here's Activity, which also needs to keep track of the variables in its equation:

class Activity<K extends string = never> {
    id: string;
    equation: Equation<K>;
    fixedValues: Partial<Record<K, number>>;

    constructor() {
        this.id = "test";
        this.fixedValues = {};
        this.equation = new Equation({ variables: [] });
    }

    public setEquation<L extends string>(equation: Equation<L>): Activity<L> {
        const that = this as Activity<any> as Activity<L>;
        that.equation = equation;
        return that;
    }

    public addFixedValues(fixedValues: Partial<Record<K, number>>): this {
        this.fixedValues = {
            ...this.fixedValues,
            ...fixedValues
        };

        return this;
    }
}

And now we can demonstrate the fluent interface, only reusing variables if the operation does not produce a value of a different type:

const equation1 = new Equation({ variables: ["a", "b"] })
const activity1 = new Activity().setEquation(equation1);
activity1.addFixedValues({ c: 20 }); // error! 
// ----------------------> ~~~~~
// Object literal may only specify known properties, and 'c' 
// does not exist in type 'Partial<Record<"a" | "b", number>>'
activity1.addFixedValues({ a: 20 });

const equation2 = equation1.addVariable("c"); // use new variable to represent new type
const activity2 = activity1.setEquation(equation2); // use new variable to represent new type
activity2.addFixedValues({ c: 20 }); // okay now

Look good!

Playground link to code

🌐
SamanthaMing
samanthaming.com › tidbits › 37-dynamic-property-name-with-es6
How to Set Dynamic Property Keys with ES6 🎉 | SamanthaMing.com
Alright back to our emoji example. Let's take a look at the output. let cake = '🍰'; let pan = { [cake]: '🥞', }; // Output -> { '🍰': '🥞' } Unfortunately, when you're using an Emoji as a key, you won't be able to use the dot notation.
🌐
Stack Overflow
stackoverflow.com › questions › 55835766 › create-an-object-with-dynamic-key-name
typescript - Create an object with dynamic key name - Stack Overflow
... var myObj: Record<string, { name : string, city: string }> = { dynamicName: { name: "test", city: "testCity" } } or you could do var myObj: Record<...> = {}; myObj["dynamicName"] = { name: "test", city: "testCity" };