Simplifying Complex Type Display in TypeScript and VS Code
Tags: typescript, types, generics, cleancode
Published: 2024-05-24 10:40:05


### Introduction:

Complex typescript types can result in a huge confusing mess when hovering on a variable.

This can make it hard to quickly understand the type information.

In this article, we'll explore techniques that can help simplify the display of complex types in VS Code using TypeScript.

### The Problem:

Consider the following example where we have a `State<T>` type and a `createState` function:

```typescript
export type State<TStateValue extends StateValue> = {
	// ...
	get: <
		TSelector extends (state: TStateValue) => unknown = (
			state: TStateValue
		) => TStateValue,
	>(
		selector?: TSelector
	) => TSelector extends (state: TStateValue) => infer TReturnType
		? TReturnType
		: TStateValue;
	// ...
};

function createState<T extends StateValue>(initialValue: T): State<T> {
	// ...
}
```

When using the `createState` function to create a store with a specific type, like `createState(0)`, VS Code expands the type and displays a verbose and complex type definition:

```typescript
const numberStore = createState(0);
/*
numberStore: {
  get: <TSelector extends (state: number) => unknown = (state: number) => number>(selector?: TSelector | undefined) => TSelector extends (state: number) => infer TReturnType ? TReturnType : number;
  set: (newValueOrFn: number | ((prev: number) => number)) => void;
  use: <TSelector extends (state: number) => unknown = (state: number) => number>(selector?: TSelector | undefined, equalityFn?: EqualityChecker<...> | undefined) => TSelector extends (state: number) => infer TReturnType ? TReturnType : number;
  onChange: (callback: (value: number, prevValue: number) => void, options?: OnChangeOptions<...> | undefined) => UnsubscribeFn;
}
*/
```

### The Solution: making the type more Opaque

To simplify the display of complex types in VS Code, we can leverage TypeScript's intersection types and a clever type definition. Here's how it works:

1. Define a `Simplify` utility type:

```typescript
export type Simplify<T> = T extends any[] | Date
	? T
	: {
			[K in keyof T]: T[K];
		} & {};
```

This type expands objects to show all the key/value types. Given an empty object, it will resolve to an empty object.

2. Intersect your complex type with `Simplify<{}>`:

```typescript
export type State<TStateValue extends StateValue> = {
	// ...
} & Simplify<{}>;
```

By intersecting the `State<T>` type with `Simplify<{}>`, we trigger a mechanism in VS Code that simplifies the displayed type when hovering over variables or types, without altering the actual type.

3. Use the modified type in the `createState` function:

```typescript
function createState<T extends StateValue>(initialValue: T): State<T> {
	// ...
}

const numberStore = createState(0);
// Hovering over `numberStore` will display `State<number>` instead of the expanded type
```


When using the `createState` function with the modified `State<T>` type, VS Code will display the simplified type name `State<number>` instead of the verbose type definition when hovering over `numberStore`.

Note: from my experiments, using `type NumberStore = State<number>` does not achieve the same result. It seems to only work when the type is a return of a function.

### Achieving the Reverse effect: expanding the type

Sometimes you may want to expand the whole type to show all the values on hover, instead of showing the `Type<Generic>`.

The `Simplify` generic type can help you achieve this

``` typescript
type Input = SomeBuilderType<SomeSuperComplexParsingFunction>
// when you hover on the input, it's to know what the input type is

type ExpandedInput = Simplify<Input>
// ExpandedInput = { id: number } 
```

### Conclusion:

The workaround presented in this article can help simplify the display of complex types in VS Code when working with TypeScript. 


I hope this article helps you navigate and simplify the display of complex types in your TypeScript projects using VS Code. Happy coding!