In some ways, Typescript is pretty awesome:
- Typescript makes Javascript more strongly typed, which means you get to catch silly typos early. This is great.
- Because of this typing information you can enjoy IDE advantages like autocompletion of object members, jumping to type definitions, or to function call sites. Nifty.
- You can easily find unused code, and delete it confidently.
- Refactoring code becomes way easier. Renaming identifiers is trivial, and when you really start to move code around Typescript is a real lifesaver. Those red squiggles turn an hour of tedious and maximum concentration refactoring work into a breezy 10 minute editing session.
- Type inference means you don’t have to tediously define the type for every variable.
But Typescript is not without downsides:
- You have to wait for compilation to finish every time you make a trivial change. Annoying on small projects, really annoying for bigger projects.
- You get bloated Javascript code along with source maps for debugging. Extra layers of complexity that make debugging harder. If you want to debug an exception trace on a release server you now have to debug an ugly Typescript call stack.
- Typescript includes Classes and Inheritance and other functionality that overlaps with modern ES6 Javascript. The additional features Typescript enables, like Class Interfaces and Enums, aren’t that compelling. The days of IE6 ECMAScript are long behind us. Modern Javascript is everywhere it’s a pretty good language that doesn’t need Typescript extensions.
What if you could get almost all the benefits of Typescript without Typescript? Turns out that you can. The trick? You just enable typescript mode in VSCode for plain ES6 Javascript projects. I just assumed that Typescript would only do typescript-y things for Typescript projects, but that’s not the case!
The trick is to add a jsconfig.json file to your project, that looks something like the one below. The “checkJS” property is the magic one that gets you 90% of Typescript in your ES6 projects. In my case, I’ve also added some extra type libraries like “ES2021” and “ESNext” (I’m not sure you actually need to declare both). Turns out Typescript otherwise doesn’t know functions like Array.at
that are already pretty widely supported in browsers.
My jsconfig.json
{
"compilerOptions": {
"target": "ES6",
"checkJs": true,
// Tells TypeScript to read JS files, as
// normally they are ignored as source files
"allowJs": true,
"lib": ["ES6", "DOM", "ES2021", "ESNext"],
"alwaysStrict": true,
// Generate d.ts files
"declaration": true,
}
}
The benefits of typescript without typescript, visualized:
A few limitations
- Sometimes you have to nudge typescript into interpreting types correctly with JSDoc type annotations. You can define nullable types
/** @type {MaybeMyClass?} */
, type unions, for example if a variable is either a bool or a string:/** @type {boolean|string} */
. Usually VSCode will suggest the correct annotation via call site type inference. - No template types or other advanced stuff. I don’t think they’re worth much anyway.
- When Typescript gets in your way you can just ignore it, so really, you get almost all the upside with none of the downside.
In conclusion
Maybe none of this is news to you. In that case, sorry to disappoint. However, if you have never experienced Typescript error checking, refactoring, and navigation tooling in an ordinary and plain Javascript project you’ve got to try it. It doesn’t happen often that a piece of tech turns out to be way better than expected, but this is one of those cases. Typescript without Typescript is amazing and I’m sticking with it.