Fix intellisense to provide correct list on properties of an object extending an interface/type

Viewed 32

I am using VSCode for building TypeScript applications. The object members do not show up when it is extending an interface or type. Is it a problem with VSCode or TypeScript, or am I doing something wrong here?

Refer code below.

interface A {[key:string]: number}
type B = {[key:string]: number}

const a:A = {'test': 100}
const b:B = {'test': 100}
const c = {'test': 100}

console.log(a.test) // intellisense not working
console.log(b.test) // intellisense not working
console.log(c.test) // intellisense working
1 Answers

TypeScript and VSCode are working as designed here.


When you annotate a variable with a non-union type* (such as const a: A = {...} and const b: B = {...}), you are telling the compiler to treat that variable as having that type regardless of what value you initialize it with, or what properties may be added or removed from it later. The compiler will not attempt to keep track of the "actual" or "current" value of the variable. It only knows the type.

Types A and B have only string index signatures whose value type is number. Variables of those types may have any number (including zero) of string-keyed properties, as long as the types of those properties are number. They have no known keys. You could delete properties (e.g., delete a.test) or add new number-valued properties (e.g., b.foo = 123).

Since A and B do not have known keys and are non-union types, the compiler has no idea what a and b's "actual" or "current" property keys are. And so there is nothing for IntelliSense to show.


On the other hand, when you do not annotate a variable, TypeScript tries to infer its type based on the inferred type of its initializer. For const c = {test: 100}, the initializer {test: 100} is inferred to have type {test: number}, and therefore c is inferred to be of type {test: number}. This is a more specific type than A or B. It is known to have a property whose key is "test" and whose value is of type number. You can't write delete c.test or c.foo = 123 without getting a compiler warning.

Since c has a known "test" key, IntelliSense can show this to you.


That means, if you want the compiler to keep track of the properties in a variable, you shouldn't annotate the variable. If you are wondering how you can guarantee that a and b are of type A and B if you don't annotate them, this is a known missing feature in TypeScript, with various workarounds. See microsoft/TypeScript#47920 and Can you check an object against a type, without assigning it said type? for more information.


* When you annotate a variable with a union type, on the other hand, the compiler performs what is known as control flow analysis to narrow the apparent type of the variable to one or more of the members of the union. This behaves more like you were expecting, where assigning a value to a variable causes the compiler (and thus IntelliSense) to know more about it than its annotated type.
Related