Reputation: 389
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?
Upvotes: 2
Views: 533
Reputation: 249656
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];
});
Upvotes: 2