DEV Community

Mastering JS
Mastering JS

Posted on

3 TypeScript Tricks You Can Use in JavaScript

Alt Text

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
}
Enter fullscreen mode Exit fullscreen mode

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'
};
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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>;
Enter fullscreen mode Exit fullscreen mode

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' }
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (0)