This Summer I wrote a number of blog posts for my TypeNotes project. Here are a few things I learned about TypeScript that were worth revisiting.
- Path aliases in TypeScript
- Use cases for JavaScript loose equality (==)
- Inspecting TypeScript’s inferred types
- Why TypeScript error codes aren’t documented
Here is a summary of each thing I learned.
Path Aliases in TypeScript
Path aliases are the modern way to do aliases and they are worth adopting on your team.
You may have seen imports like this in an app:
// app.tsx
import {Page} from '../../views/pages/Page';
Those ../../ paths get worse as the tree grows, and they break when you move
files. Path aliases fix that by resolving from a fixed root (usually src/).
Here’s that same import with an aliased path:
// app.tsx
import {Page} from '@/views/pages/Page';
The @ is a convention that represents a directory in your project, typically
src/. Both TypeScript and your project’s build tooling need to know how to
resolve it.
Implementing this is a bit different in every framework, so the best plan is to search “path aliases in <your framework>”. The TypeScript implementation will look something like this:
// tsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
- Path aliases in TypeScript – TypeNotes
Use cases for JavaScript loose equality
Should we ever use two equals as a JavaScript comparator? I found two use cases.
The first is comparing a value to null or undefined:
if (value == null) {
// Value is either null or undefined
}
if (value === null || value === undefined) {
// This is equivalent
}
The second is comparing identifiers that could be numbers or strings, such as those from an API:
const id = element.dataset.userId; // string
if (id == 42) {
// true
}
The first example is more widely accepted. If you find implicit type coercion less readable than the alternative, as I do, I’d suggest avoiding them both.
- JavaScript loose equality (==) and type coercion – TypeNotes
Inspecting TypeScript’s inferred types
What does TypeScript do when you don’t explicitly type something? We can find out.
These two lines of code, one explicitly typed and one not, should be equivalent.
// explicitlyTyped.ts
let greeting: string = 'hello';
// implicitlyTyped.ts
let greeting = 'hello';
But what’s going on under the hood? One way to answer that question is by emitting the type declaration. Consider this file with no explicit typing:
// implicitlyTyped.ts
let greeting = 'hello';
const fullName = (first: string, last: string) => first + ' ' + last;
const answer = 42;
We run it through the tsc compiler with the --declaration and
--emitDeclarationOnly flags— output the declaration, and only the
declaration):
npx tsc implicitlyTyped.ts --declaration --emitDeclarationOnly
Which produces this:
// implicitlyTyped.d.ts
declare let greeting: string;
declare const fullName: (first: string, last: string) => string;
declare const answer = 42;
There are a couple of neat things going on here:
- We used
let, sogreetingcan be re-assigned. TypeScript typed it as astring. fullNameconcatenates two strings, so TypeScript determined thatfullNamemust return a string, and typed it.answeris a constantconst, so TypeScript used a42number literal.
The last example is instructive. With const, TypeScript can infer a literal
type (42) instead of the wider number. That’s one more argument in favor of
(almost) always using const: tighter inferred types.
- Inspecting TypeScript’s inferred types – TypeNotes
Why TypeScript error codes aren’t documented
Why aren’t TypeScript’s errors documented? There are a few good reasons.
Here’s some unsafe TypeScript code.
// greeting.ts
let greeting: string = null;
Hover on greeting, and you’ll see the following:
Type 'null' is not assignable to type 'string'. (tsserver 2322)
That’s pretty much all we get. The error is an error, and you need to fix it.
Why won’t the TypeScript team make this easier? The “why” has several explanations I found compelling. Terse error codes:
- Avoid a major documentation burden.
- Avoid inviting engineers to suppress errors.
- Acknowledges that errors are dynamic and hard to explain well.
Understanding the “why” of this choice and writing about what you should do instead— build your own mental models about type safety— was a good exercise for me.
- Why TypeScript error codes aren’t documented – TypeNotes