**Type Checking Without the Muck**
In this, the fourth installment of our series on hacking with minimal tooling and maximum web platform-ing at sightread.org, we're going to tackle a topic that may have you yawning: types. Specifically, type checking without the verbosity and noise that often accompanies it.
As a developer who's worked with various languages, I must confess that I've always had a... let's say, complicated relationship with types. On one hand, they offer a level of safety and reassurance when working on complex projects. On the other hand, I find myself longing for the simplicity of earlier days in my coding journey.
But before we dive into the world of type checking, let me share a little anecdote from my friend Jeff Lembeck: "For personal projects, types are your best friend. They provide context when you're not around to remember it." And I must admit, as much as I dislike adding more complexity to my codebase, Jeff has a point.
Now, on to the main event. What I especially detest in languages like TypeScript is their verbosity. The type casting, the noise, and the mental load that comes with reading through lines of extraneous code are all factors that make me want to scream "Gimme a break!"
I still recall when I first encountered the DOM type-casting issues in TypeScript. "I don't like all this 'as'-ing about," I thought to myself, as the syntax seemed to clutter up my otherwise simple code.
So, can we have some level of type safety without all the fuss? The answer, it turns out, is yes – mostly. Let's start with a blast from the past: JSDoc. Before TypeScript and Flow came onto the scene, JSDoc was already doing its thing, providing inline documentation that looked suspiciously like types.
And guess what? Both Flow and TypeScript have supported JSDoc out of the box for years! This means we can reuse existing code instead of redefining types with a new syntax. But does TypeScript still support JSDoc in 2023?
A quick investigation reveals that, yes, TypeScript still supports JSDoc – using the `-p` flag with `npx`. No need to install anything locally; it's all cached and ready to go!
So how do we leverage JSDoc for type safety? Here's a step-by-step guide:
- Create a `types.d.ts` file in your project's root directory.
- Use JSDoc syntax to annotate variables and functions with types. For example: `/** @type {string} */ var name;
- Import the shared type definitions from `types.d.ts` into your JavaScript files using JSDoc import statements, like this: `import { name } from './types';`
Now things get interesting. TypeScript's DOM types are strict – and sometimes too strict for our taste. For instance, when trying to access an element's value property, TypeScript throws a fit.
The "traditional fix" is inline casting, but I'd rather not clutter up my code with `as` keywords. So what's the alternative?
// In types.d.ts
interface Element {
// Add value property
value: string;
}
// Now use element.value as normal in your JavaScript files!
This approach may seem less strict, but it works for me – and my DOM. I know which elements have which properties, so why add unnecessary noise?
Another trick up our sleeve is to extend built-in interfaces like `Window` or `Event`. Let's take service workers as an example:
// In types.d.ts
interface Window {
// Add skipWaiting method (optional)
skipWaiting: () => void;
}
// Now self.skipWaiting() works without errors in your JavaScript files!
So, how about strict null checks? With `strictNullChecks` enabled, TypeScript wants you to handle every possible null scenario. But do I really need to sprinkle `?.` and `if` statements everywhere?
// No thanks! Leave the DOM to moi.
const element = document.getElementById('myId'); // No more casting required!
And that's not all – we can also disable implicit any types, which means some function parameters will be left without explicit types. Not a problem for me!
Finally, let's talk about test file checking. In my testing setup, I use mock data that doesn't satisfy strict types. And you know what? That's fine by me.
With this approach, we get the benefits of type safety without all the extra hassle. All the type overhead is tucked away in `types.d.ts` and JSDoc comments, making it easy to maintain a clean codebase.
So, can you have your cake (type safety) and eat it too (without the verbosity)? Absolutely! And that's why I'm excited to share this approach with everyone – from personal projects to large-scale teams.
Find me on various platforms if you want to discuss further: BlueSky, Mastodon, LinkedIn, Threads, Twitter. Happy coding!