Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: Index signature is missing in type

I want MyInterface.dic to be like a dictionary name: value, I define it as follows:

interface MyInterface {     dic: { [name: string]: number } } 

Now I create a function which waits for my type:

function foo(a: MyInterface) {     ... } 

And the input:

let o = {     dic: {         'a': 3,         'b': 5     } } 

I'm expecting foo(o) to be correct, but the compiler is falling:

foo(o) // Typescript error: Index signature is missing in type { 'a': number, 'b': number } 

I know there is a possible casting: let o: MyInterface = { ... } which do the trick but the question is, why typescript is not recognizing my type?


Extra: works fine if o is declared inline:

foo({    dic: {     'a': 3,      'b': 5   } }) 
like image 977
Manu Avatar asked May 03 '16 13:05

Manu


People also ask

What is an index signature TypeScript?

The index signature is a fitting way to handle objects with properties we know nothing about. Its syntax describes a regular property, but instead of writing a standard property name, we define the type of keys and the properties.

Is not assignable to parameter of type record string unknown?

The "Type 'unknown' is not assignable to type" error occurs when we try to assign a value with a type of unknown to a value of a different type. To solve the error, use a type assertion or a type guard to verify that the two values have compatible types before the assignment.


2 Answers

The problem is that when the type is inferred, then the type of o is:

{ dic: { a: number, b: number } } 

That's not the same as { dic: { [name: string]: number } }. Critically, with the top signature you're not allowed to do something like o.dic['x'] = 1. With the 2nd signature you are.

They are equivalent types at runtime (indeed, they're the exact same value), but a big part of TypeScript's safety comes from the fact that these aren't the same, and that it'll only let you treat an object as a dictionary if it knows it's explicitly intended as one. This is what stops you accidentally reading and writing totally non-existent properties on objects.

The solution is to ensure TypeScript knows that it's intended as a dictionary. That means:

  • Explicitly providing a type somewhere that tells it it's a dictionary:

    let o: MyInterface

  • Asserting it to be a dictionary inline:

    let o = { dic: <{ [name: string]: number }> { 'a': 1, 'b': 2 } }

  • Ensuring it's the initial type that TypeScript infers for you:

    foo({ dic: { 'a': 1, 'b': 2 } })

If there's a case where TypeScript thinks it's a normal object with just two properties, and then you try to use it later as a dictionary, it'll be unhappy.

like image 149
Tim Perry Avatar answered Sep 16 '22 19:09

Tim Perry


TS wants us to define the type of the index. For example, to tell the compiler that you can index the object with any string, e.g. myObj['anyString'], change:

interface myInterface {   myVal: string; } 

to:

interface myInterface {   [key: string]: string;   myVal: string; } 

And you can now store any string value on any string index:

x['myVal'] = 'hello world' x['any other string'] = 'any other string' 
like image 41
C. Lewis Avatar answered Sep 17 '22 19:09

C. Lewis