Skip to main content
  1. « Go back to Articles

Forcing IntelliSense to resolve a type definition

Image of the Expand type definition

I’m using intersection types very often to build up new types from other types by extending them. Here’s an example:

1
2
3
4
5
6
7
type DropdownItem = {
  label: string
}

type NumericDropdownItem = DropdownItem & {
  value: number
}

However, even with this simple example, the IntelliSense tooltip shows just the literal type definition without explicitly resolving the intersection type:

IntellisSense tooltip showing the literal intersection type definition

This is not very helpful when encountering this type somewhere and we just want to know which properties are contained, especially for more complex or even mapped types.

Here is a type alias that forces IntelliSense / TypeScript to expand a type.

type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never

When we use it in the declaration of the NumericDropdownItem type from above,

1
2
3
4
5
type NumericDropdownItem = Expand<
  DropdownItem & {
    value: number
  }
>

the IntelliSense tooltip becomes much more helpful:

IntellisSense tooltip showing all properties of the expanded type

The Expand type uses conditional type inference to make a copy of type T into a new type variable O and then a mapped type which acts essentially as identity type that iterates through the copied type’s properties.

The conditional type inference is used to distribute union types and to force the compiler to evaluate the true branch of the conditional. Without it, sometimes the compiler will just output the mapped type { [K in keyof SomeTypeName]: SomeTypeName[K] }, which is not what we want to see.

The Expand type defined above only works on top-level properties. In case we want to expand the proprety types as well, we can easily define a recursive variant. The T extends object conditional is added to not unneccessarily drill down into primitive types.

1
2
3
4
5
type ExpandRecursively<T> = T extends object
  ? T extends infer O
    ? { [K in keyof O]: ExpandRecursively<O[K]> }
    : never
  : T

This helper type can improve the developer experience, but should only be used where individual property names need to be looked up very often. Conceptually, it is a no-op, but repeated use can add up computationally and increase the compile time quite a lot, especially the recursive variant.

The article’s code can be found in this TypeScript Playground.

Dr. Ole Hüter
Author
Dr. Ole Hüter
Freelance Full Stack Web Developer