What im trying to archive is better DX by expanding the types for parameters in a function signature, essentially i want the tooltip when hovering over the following function:
type Props = {
a: number;
};
const func = (props: Props) => {};
to tell me this
const func: (props: {
a: number;
}) => void
instead of the unexpanded type
const func: (props: Props) => void;
I cant inline the type in the signature because of the coding standards in the project im working on.
What i noticed by playing around with different functions and the Prettify
helper (Matt Pocock) is that using a generic function, all parameters that make use of the generic will get expanded, if the the prop type is fed into Prettify
like this (notice the generic isnt actually used in props):
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
type PropsGeneric<T> = Prettify<{
a: number;
}>;
const funcGeneric = <T extends any>(props: PropsGeneric<T>) => {};
this will expand props in the signature:
const funcGeneric: <T extends unknown>(props: {
a: number;
}) => void
I also noticed that using any other combination, using function statements instead of arrow functions, leaving out prettify
, not using the declared generic in the signature, will stop the props from expanding.
Is there a way to archive the expanded prop in the tooltip, without unneccessarily using generics in the function signature in TypeScript 5.4?
And if so, is there a more robust way that also works with function statements?
There is no documented surefire way to get IntelliSense to display a type exactly the way you'd like it to. There are various open feature requests to allow more user-configurable type display, such as microsoft/TypeScript#28508, microsoft/TypeScript#31940, and microsoft/TypeScript#45954, but I don't see any indication that this is being worked on.
There are various tricks to try to influence this behavior, such as those described at How can I see the full expanded contract of a Typescript type?, but these only work in certain scenarios, and the ones from that issue don't work for your example. The only way I know to figure out exactly why a particular type is displayed a particular way would be to walk through the compiler implementation. It's not something very well documented, and it changes from time to time. Technically it is the compiler's prerogative to display a type however it wants, as long as the displayed type is accurate.
For your example as given, I'd be inclined to write an inline mapped type to cause it to be eagerly evaluated for display:
const func = (props: { [K in keyof Props]: Props[K] }) => { };
/* ^? const func: (props: {
a: number;
}) => void */
As you can see, that works. But it might not be "robust" enough, in light of the above information. For all I know, in the next version of TypeScript, the above type will be displayed as-is like { [K in keyof Props]: Props[K] }
instead of like {a: number}
. So this sort of thing is always going to be an art and not a science, at least until and unless one or more of the GitHub issues listed above are implemented.