TypeScript is growing rapidly in popularity, but isn't worth the effort for some projects. However, even if you're writing JavaScript, there's some patterns you can learn from TypeScript. Here's 3 of our favorite TypeScript-inspired patterns for JavaScript.
1) JavaScript Enums
TypeScript has support for enums, which are a neat pattern for defining an object whose keys you can use in place of hard-coded strings.
enum Direction {
Up,
Down,
Left,
Right
}
JavaScript doesn't support enums. But TypeScript compiles to JavaScript, so what does the above code turn into? Turns out TypeScript enums compile into JavaScript POJOs.
const Direction = {
Up: 'Up',
Down: 'Down',
Left: 'Left',
Right: 'Right'
};
You can also make Direction
immutable using Object.freeze()
, which makes Direction
pretty close to a TypeScript enum. And that means you can do what you expect to do with enums in other languages:
- Get allowed enum values:
Object.keys(Direction)
- Check if a value equals an enum value:
val === Direction.Up
- Check if a value is in the enum:
Object.hasOwnProperty(val)
2) orFail()
helpers to avoid null checks
TypeScript requires you to check for null
query results in Mongoose. This is good practice, but also gets a bit cumbersome if you need to do it over and over again.
const doc = await Model.findOne();
// TypeScript reports an error "Object is possibly 'null'."
// Need to add a `if (doc != null)` check
doc._id;
Mongoose queries have a neat orFail()
helper that throws an error if there's no result, which means you can go about using doc
without explicitly checking for null
. This is because orFail()
marks the query as resolving to a NonNullable.
const doc = await Model.findOne().orFail();
// Works!
doc._id;
We use this orFail()
pattern a lot in our TypeScript tests, because it saves us from having to add repetitive if
checks. If the orFail()
is triggered, the error bubbles up to error handling.
However, there's no reason why you can't use orFail()
in JavaScript! Just because TypeScript isn't there to tell you there's a problem, doesn't mean the problem isn't there.
Similarly, if you have other functions that may return null
if a value is not found, consider wrapping them in a function that throws an error if the value is not found. It can save you a lot of null
checks!
3) Use JavaScript maps for objects with unknown types
TypeScript makes it just a little easier to define a map with arbitrary keys than an object with arbitrary keys.
// An object with string keys and values of type `T`
interface AnyObject<T = any> { [k: string]: T }
// Map with string keys and values of type `T`
type AnyMap<T = any> = Map<string, T>;
TypeScript makes working with maps to store arbitrary key/value mappings easier, and with good reason: maps support mapping from keys of arbitrary type.
const fakeMap = {};
fakeMap[1] = 'number';
fakeMap['1'] = 'string';
fakeMap; // { '1': string }
const map = new Map();
map.set(1, 'number');
map.set('1', 'string');
map; // Map(2) { 1 => 'number', '1' => 'string' }
The issue is that JavaScript object keys can only be strings or symbols, so JavaScript always converts object keys to strings. That's why you should use maps in cases where you aren't sure that the keys you're using are strings.
Top comments (0)