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


phifa
  • 856
  • 7
  • 11

1 Answers1

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

KiraLT
  • 2,385
  • 1
  • 24
  • 36
  • 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' – phifa Oct 29 '20 at 09:26
  • 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. – KiraLT Oct 29 '20 at 10:06
  • 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. – phifa Oct 29 '20 at 13:01
  • Added a third example. It's not the best, but it should work. – KiraLT Oct 29 '20 at 14:20
  • 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? – phifa Oct 30 '20 at 11:24
  • Added an explanation after the last example. – KiraLT Oct 30 '20 at 12:12
  • thanks for the update. The problem I have, I do not know the name of the object key, so I need to access the property with the variable : `if (typeof config.myObj[someVar] === 'object')` but this will throw TS errors and not give me autocomplete. Here a codesandbox: https://codesandbox.io/s/crimson-morning-38y19?file=/src/index.ts – phifa Nov 02 '20 at 10:41
  • Assign to variable and then do the checking: ``` const myvar = config.myObj[someVar] if (typeof myvar === "object") { console.log(myvar.firstProp.trim()); } ``` – KiraLT Nov 02 '20 at 11:59