1

I have a type in typescript, that is an object, for which all of the properties should be of type number. Based on this type, I would like to create various interfaces, which are more concrete then the original type, and pass them as generic parameters to a class, which excepts a generic that extends my basic type, but I always get the following typescript error:

Index signature for type 'xxx' is missing in type 'yyy'

Is something similar to what I want to do is possible in typescript? The best I was able to achieve, is telling my moreConcrete interface, that it extends the basic type, this way, the error goes away, but I lose autocompletion and other intellisense features when trying to use the interface.

Here's a fiddle with an example: Fiddle

And here's the code in the fiddle:

type basic = {
    [key: string]: number
}

class A<TInput extends basic> {

}

interface moreConcrete {
    a: number,
    b: number
}
const test = new A<moreConcrete>(); // this does not work like this

interface otherMoreConcrete extends basic {
    a: number,
    b: number
}
const test2 = new A<otherMoreConcrete>(); // this does not give any errors
const typeTest: keyof otherMoreConcrete = 'as'; // this accepts as as a key of otherMoreConcrete, because of the extension to `basic`, this should be an error
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Adam Baranyai
  • 3,635
  • 3
  • 29
  • 68
  • @kaya3 the usage of the interface is ommited here in the class, as the problem is reproductible without it also. my original `basic` interface is not so simple in the real code neither, this is just to try to show in an easy way the problem I am experiencing. Basically, what I would need, is can I define a type that constraints an interface that is extending that type? – Adam Baranyai Nov 14 '21 at 11:41
  • You could restrict generic parameter of class, so it will error if you'll pass something with non numeric member `class A>` https://www.typescriptlang.org/play?#code/MYGwhgzhAECCA8AVaBTAHgFxQOwCYwCUVgB7AJ13gGsUBPEgM2kQBppsBXAWwCMUyAfAOgBvAL4AoCQEtsWMgzDAU0LuRQBhEtmBkUWUROjHoYAFztufMiyMmeFzr362T0YBYgYysgOYTJUmwvaCwQgF52FAB3OHg1PS0dPSwBAAoASgBuKVl5RWVoEgwAC34AWXUk3X0VETtjc0tnGwboB2brAIkgkLCMACZoSOwYuOKyskrE7RrUzJzejFDaAAcURBQvCxp6JgmKqtmUlUiAckgzrKA – Aleksey L. Nov 14 '21 at 11:52

1 Answers1

2

In order to better understand this problem, please see this answer.

TL;TR

interfaces have no indexing by the default, whereas types have. In order to get rid of an error, just use type instead of interface. I mean, instead of using:

interface moreConcrete {
    a: number,
    b: number
}

use

type moreConcrete= {
    a: number,
    b: number
}
const test = new A<moreConcrete>(); // ok

You can find extra examples in my article

Is this super safe to use type instead of interface ? I don't think so. It all depends on your code and what you want to achieve. I'm not aware of all edge cases. So, it should be thoroughly tested.

It might be safe to use type instead of interface in this particular case because A class expects indexed type.

As for the second problem:

interface otherMoreConcrete extends basic {
    a: number,
    b: number
}

const test2 = new A<otherMoreConcrete>(); // this does not give any errors
const typeTest: keyof otherMoreConcrete = 'as'; // this accepts as as a key of otherMoreConcrete, because of the extension to `basic`, this should be an error

You don't have error here, because there is a big difference between otherMoreConcrete interface and moreConcrete. otherMoreConcrete extends basic. It means that it has indexed property. I mean apart from having a and b it accepts any string key.


type otherMoreConcrete = {
    [x: string]: string;
    a: "a";
    b: "b";
}

Hence, if you want to trigger en error in your last example, just use type instead of interface:

type basic = {
    [key: string]: number
}

class A<TInput extends basic> {}


type moreConcrete = {
    a: number,
    b: number
}
const test = new A<moreConcrete>(); // ok

type otherMoreConcrete = {
    a: number,
    b: number
}


const test2 = new A<otherMoreConcrete>(); // ok
const typeTest: keyof otherMoreConcrete = 'as'; // error

Playground