2

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',
  },
};


4
  • 2
    [someVar]: is the same as SomeKey:. So type it as SomeKey: {firstProp: string, secondProp: string} Commented Oct 29, 2020 at 8:34
  • so you are saying what should my interface look like? Commented Oct 29, 2020 at 8:38
  • is someVar a dynamic string? Commented Oct 29, 2020 at 8:45
  • @zerkms cannot use your suggestion, as someVar is dynamic. Commented Oct 29, 2020 at 13:02

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

8 Comments

when i use your last answer, the tsc compiler says: Type '{ SomeKey: { firstProp: string; secondProp: string; }; thirdProp: string; }' is not assignable to type '{ dynamic: { [key: string]: { firstProp: string; secondProp: string; }; }; thirdProp: string; }'. Object literal may only specify known properties, and '[someVar]' does not exist in type '{ dynamic: { [key: string]: { firstProp: string; secondProp: string; }; }; thirdProp: string; }'.ts(2322) The expected type comes from property 'myObj' which is declared here on type 'MyConfig'
If you are using the last example, you will need to change how you store data (I updated the last example). As you can see I wrapped [someVar]: {} block to dynamic for typing convenience.
two problems: 1. option does not work, as someVar is dynamic 2. option does not work, because i cannot change the structure of the object.
Added a third example. It's not the best, but it should work.
i see, it's working. i will mark it as the correct answer. Can you explain to me: ``` [key: string]: { firstProp: string secondProp: string } | string ``` why is | string needed?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.