IDE Inlay Hints
Am I weird in disliking inlay hints?
They're those little notes that your IDE can add to your code to show you what types things are, but they're not actually part of your source code. For an example, see TypeScript v4.4's documentation for inlay hints.
My opinion is that:
- for well-written code, they fill my screen with redundant noise that makes it more difficult to see the simple code in front of me.
- for poorly-written code, they're a crutch that you can rely on instead of refactoring the code to be more readable and less fragile.
As an example, take this code:
function main() {
console.log(foo("Hello", "world"))
}
// Imagine this function is in some other file, so it's not on the same screen.
function foo(target: string, greeting: string) {
return `${greeting}, ${target}!`
}
If you're looking at just the call site, there's a non-obvious bug here because the foo()
function takes two arguments of the same type, and the author of main()
passed them to foo()
in the wrong order.
Inlay hints propose to help with the issue by showing you function parameter names inline at your call site, like this:
function main() {
console.log(foo(target: "Hello", greeting: "world"))
}
(target:
and greeting:
are added, and somehow highlighted to indicate that they're not code.)
Now it's more clear that you've got the arguments in the wrong order. But only if you're looking at the code in an IDE that's providing those inlay hints. If you're looking at just the raw source code (say, while doing code review, or spelunking through Git history), you don't see those hints. The developer is relying on extra features to make only their own workflow easier.
Without inlay hints, it's a bit more obvious that, hey, the ordering here can be ambiguous, I should make that more clear. Maybe we should make foo()
more user-friendly?
Lots of languages support named parameters for this reason. TypeScript/JavaScript don't have named parameters directly, but often end up approximating them with object passing:
function foo({target, greeting}: FooArgs) {
return `${greeting}, ${target}!`
}
interface FooArgs {
target: string
greeting: string
}
Now the call site is unambiguous without inlay hints:
foo({greeting: "Hello", target: "world"})
And, even better, our arguments can be in whatever order we want. (This syntax is even nicer in languages like Python or Kotlin that have built-in support for named parameters.)
The prime use of these kinds of hints is when you're forced to use some library that you didn't write that has a poor API. But IMO you're probably still better off writing your own shim that uses better types and/or named parameters to operate with that library, to save yourself the continued headache of dealing with it. Inline hints just let you pretend it's not a problem for just long enough to pass the buck to the next developers that have to read/modify the code.