I'm really curios if that somehow a design gap in TypeScript. Let's say I have a function, that takes any objects that fulfills this interface
interface Params {
[key: string]: string | number | boolean | undefined | null;
}
So the key must be a string and the type of the properties can be a primitive or void.
If I now specify some method that takes a specific interface which as such fulfills the Params interface I get the error, that the other interface doesn't have an index signature. So the other interface may simply look like.
interface Foo {
bar: string;
}
I also tried to change the signature to Record<string, string | number | boolean | undefined | null>
but that also gives the missing index signature error.
My whole code then may look like this. To give a full picture. So I need to have the key and value type of the object specified to be able to do certain operations in Object.entries
function specificFunction(obj: Foo) {
genericFunction(obj);
}
function genericFunction(params: Params) {
Object.entries(params).forEach(([key, value]) => {
…
})
}
EDIT: Important note, the behavior of the specificFunction should stay the same, as it should be used to narrow done the allowed interface, because in this special case only objects with certain properties are allowed to ensure a certain result.
To define an object of objects type in TypeScript, we can use index signatures with the type set to the type for the value object. const data: { [name: string]: DataModel } = { //... }; to create a data variable of type { [name: string]: DataModel } where DataModel is an object type.
The error "Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature" occurs when we try to access a property that doesn't exist on the global object. To solve the error, extend the global object and add types for the necessary properties.
Index signature is used to represent the type of object/dictionary when the values of the object are of consistent types. Syntax: { [key: KeyType] : ValueType } Assume that we have a theme object which allows us to configure the color properties that can be used across the application.
Inside the function we assign the parameters to properties in the object. To do this, we have to specify the this keyword, which refers to the calling object. The variables and parameters may have the same names. Anything we pass to the constructor as an argument, will be assigned to the property of the object.
genericFunction
expects type with index signature
.
Foo
does not have index signature by the default because it is an interface.
But there is a small hint. If you use type
keyword to declare Foo
instead of interface
- it will work.
Why?
Because type
's have index signature by the default.
See this answer.
So, next code will compile:
interface Params {
[key: string]: string | number | boolean | undefined | null;
}
// use type here instead of interface
type Foo = {
bar: string;
}
function specificFunction(obj: Foo) {
genericFunction(obj);
}
function genericFunction(params: Params) {
Object.entries(params).forEach(([key, value]) => {
})
}
Playground
UPDATE 2
If you don't have control over Params
and Foo
, you can use next utility type:
interface Params {
[key: string]: string | number | boolean | undefined | null;
}
interface Foo {
bar: string;
}
type Values<T> = T[keyof T]
type MakeIndexed<T> = {
[P in keyof T]: T[P]
}
function specificFunction(obj: MakeIndexed<Foo>) {
genericFunction(obj);
}
function genericFunction(params: Params) {
Object.entries(params).forEach(([key, value]) => {
})
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With