phifa
phifa

Reputation: 906

How to type object with index signature and dynamic variable in TypeScript?

I am new to TypeScript, and have tried various ways to type this, but running into problems with index signatures. What should the interface look like?

interface MyConfig {
...
}

// someVar can be any string
let someVar = "dynamicKey";

// the structure of the object cannot change
const config: MyConfig = {
  myObj: {
    [someVar]: {
      firstProp: 'some text',
      secondProp: 'some text',
    },
    thirdProp: 'some text',
  },
};


Upvotes: 2

Views: 2786

Answers (1)

KiraLT
KiraLT

Reputation: 2607

If you know the exact value or values for someVar variable, you can have a strict interface like:

interface MyConfig {
    myObj: {
        SomeKey: {
            firstProp: string
            secondProp: string
        },
        thirdProp: string
    }
}

Then you can use:

const someVar = "SomeKey";

const config: MyConfig = {
    myObj: {
        [someVar]: {
            firstProp: 'some text',
            secondProp: 'some text',
        },
        thirdProp: 'some text',
    },
};

But if you wish that someVar would be dynamic, it's a bit tricky. For that I would recommend you moving the dynamic part to a separate block, so you could use:

interface MyConfig {
    myObj: {
        dynamic: {
            [key: string]: {
                firstProp: string
                secondProp: string
            }
        },
        thirdProp: string
    }
}

const someVar = "SomeKey";
const config: MyConfig = {
    myObj: {
        dynamic: {
            [someVar]: {
                firstProp: 'some text',
                secondProp: 'some text',
            },
        },
        thirdProp: 'some text',
    },
};

And finally, if you have dynamic someVar and can't change the data structure. you can use following:

interface MyConfig {
    myObj: ({
        [key: string]: {
            firstProp: string
            secondProp: string
        } | string
    } & {
        thirdProp: string
    })
}
const someVar: string = "SomeKey";

const config: MyConfig = {
    myObj: {
        [someVar]: {
            firstProp: 'some text',
            secondProp: 'some text',
        },
        thirdProp: 'some text',
    },
};

// String
console.log(config.myObj.thirdProp.trim())

// Error, union string | object
console.log(config.myObj.abc.firstProp)

if (typeof config.myObj.abc === 'object') {
    // string
    console.log(config.myObj.thirdProp.trim())
    // string
    console.log(config.myObj.abc.firstProp.trim())
}

In this example, we use a typescript index signature + we specify known properties. You can also notice a strange thing - the index signature has union object | string. This is because of typescript limitation:

As soon as you have a string index signature, all explicit members must also conform to that index signature. This is to provide safety so that any string access gives the same result.

Reference: How to combine declared interface properties with custom Index Signature

Upvotes: 1

Related Questions