Published: March 01, 2023 • 2 min read
There’s been a lot of recent discussion in the TypeScript community about typing functions. These arguments tend to take binary positions: always type your functions, or never type them unless the compiler demands it.
Which is the best option? Today I’d like to present a third option you might not have considered, that I think is just right:
Most of the time, we should use implicit types. Here’s a React click handler with implicit typing:
const handleClick = () => {
alert('clicked');
};
The TypeScript compiler accepts this code without an explicit return type because it can evaluate the function and knows that it can’t return anything.
We call this not-thing void
:
const handleClick = (): void => {
alert('clicked');
};
So, is adding the type an improvement? In my opinion, no. Most click handlers don’t return anything, so adding that doesn’t tell us much.
Use implicit types when you can. Your fingers and fellow developers will thank you.
An exception to this rule is library functions. These benefit from explicit typing.
Consider this function that accepts a string of markdown and returns a React node with parsed HTML:
import React, { ReactNode } from 'react';
import ReactMarkdown from 'react-markdown';
const renderMarkdown = (markdown: string): ReactNode => {
return <ReactMarkdown>{markdown}</ReactMarkdown>;
}
My compiler makes me type the input, because ReactMarkdown
’s child must be a
string, and it can’t be sure that a string will always be passed. But I didn’t
have to type the returned ReactNode
; TypeScript can infer that from the
library because it is itself typed.
If it’s not required, why add it? I think the answer lies in how library functions differ from other kinds of functions. Library functions are meant to be:
Typing the function’s inputs and outputs supports each of these goals. It teaches programmers what the inputs and outputs should be, which can be unclear in isolation. It makes the function more reusable because it’s easier to understand. And, typing makes it just a bit harder to casually extend the function because you have to spell out the new types.
Typing your library functions is a good idea.
Maybe you’re thinking: why bother typing code if we don’t have to?
To that, I’d say code is for humans as well as machines. Just because a compiler doesn’t require something doesn’t mean it doesn’t have value. Consider unit tests, inline tests, or well-written comments— each provides value that isn’t required. We include them because they make the code more pleasant and predictable.
I think library functions in general should be over-communicated. It should be clear through as many channels as possible what they do and don’t do.
Explicit typing provides this kind of value.
Use implicit types most of the time, and type your library functions. Do you have a different approach? I’d love to hear about it.
Get better at programming by learning with me. Subscribe to my newsletter for weekly ideas, creations, and curated resources from across the world of programming. Join me today!