2
votes

Consider this code:

interface MyInterface {
  foo: string
  bar: string
  baz: string
}

const myObj: MyInterface = {
  foo: "foo",
  bar: "bar",
  baz: "baz"
};

Object.keys(myObj).forEach(obj => {
  obj = myObj[obj];
});

When enabling strict mode I get this error: TS7017: Element implicitly has an 'any' type because type 'MyInterface' has no index signature.

The easiest solution seems to be:

interface MyInterface {
  [key: string]: string;
  foo: string
  bar: string
  baz: string
}

however this opens up for any string properties in MyInterface-objects.

Then I was thinking of using a mapped type instead:

type ValidEnteries = "foo" | "bar" | "baz";

type Alternative = {
  [key in ValidEnteries]: string
}

While this seems correct to me, the original problem returns with missing index signature.

Is there any way to both have an index signature, and limit the object to a certain number of properties?

1

1 Answers

2
votes

If all you want to do is get the already existing properties of the interface, you don't need an index signature. You can index any object with a string as long as the string is a known key of the object. So this works :

 myObj['bar'] // 'bar' can be checked as a key of MyInterface so it's ok 

The problem is that Object.keys(myObj) returns string[] not Array<keyof T>. The simplest solution is to use a type assertion to let the compiler know the return of keys is an array of keys of MyInterface

Object.keys(myObj).forEach(obj => {
    let d = myObj[obj as keyof MyInterface];
});
// OR
(Object.keys(myObj) as Array<keyof MyInterface>).forEach(obj => {
    let d = myObj[obj];
});

Or if you are comfortable with the keys function always returning Array<keyof T> you can extends the global declaration

declare global { /// Needed if you are in a module otherwise omit this and the closing }
    interface ObjectConstructor {
        keys<T extends object>(o: T) : Array<keyof T>
    }
}

// Now works without any extra casting 
Object.keys(myObj).forEach(obj => {
    let d = myObj[obj];
});