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:
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:
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:
- Define a
Simplify
utility type:
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.
- Intersect your complex type with
Simplify<{}>
:
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.
- Use the modified type in the
createState
function:
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
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!
Top comments (0)