To draw an analogy, JavaScript is a jump-jet and TypeScript is an aircraft carrier; And even though TypeScript is safe, it’s still not safe enough to run a nuclear power plant.

I choose JavaScript! Why?

  1. Better composability
  2. Faster development

Type Safety Bugs

I spend about 1% of my time dealing with types and type related bugs in JavaScript. To be fair, I use Prettier, ESLint, & BDD with VS Code, so most type safety bugs get knocked down before production anyway.

Eric Elliot has a good post on this called, The TypeScript Tax.


TypeScript’s classes don’t get me excited, because I don’t use classes. I prefer JavaScript object composition.

Enclosed Pure Object Composition

I like to use Enclosed Pure Object Composition in place of Object Oriented Programming. Conceptually, the mental model is exactly the same, but with the added powers that first-class functions absorb in the Web’s event based architecture. Everything I need from OOP can be done with plain JavaScript. No prototypes. No classes necessary.

For example: here is the Enclosed Object equivalent of a Class:

// Counter.mjs

export const Counter = (count = 0) => ({
  add: () => (count += 1),
  get count() {
    return count;

const counter = Counter(2);
console.log(counter.count); // 4

This kind of object composition is easier to reason about. The memory footprint and CPU load of Object Composition is identical to Classes and Prototypes.

Let’s compose…

// Counters.mjs

import { Counter } from './Counter.mjs';

export const Counters = (...counters) => ({
  add: () => => counter.add()),
  get count() {
    return => counter.count);

const counters = Counters(Counter(0), Counter(1));
console.log(counters.count); // [ 1, 2 ]

Extensible Object Composition

We can make our pattern more extensible. Here is a similar object composition, allowing for the use of JavaScript’s this keyword.

// Employee.mjs

const methods = () => ({
  work() {
    this.product += this.productivity;

  report() {
      `I'm ${}, a ${this.role}.
       I produced ${this.product} units.`

export const Employee = name => ({
  role: 'worker',
  productivity: 2,
  product: 0,

const al = Employee('Al');;;

// I'm Al, a worker. I produced 2 units.

Let’s extend…

// Manager.mjs

import { Employee } from './Employee.mjs'

const accept = () => ({
  accept({ role, productivity }) {
    Object.assign(this, {

const al = Object.assign(

const promotion = {
  role: 'manager',
  productivity: 1

// I'm Al, a manager. I produced 1 units.

JavaScript’s this keyword is unnecessary. The same result can be achieved by passing the employee’s state to the scope of the employee’s methods.

// Employee.mjs

const work = state => ({
  work: () => {
    state.product += state.productivity;

export const Employee = name => {
  const employee = {
    role: 'worker',
    productivity: 2,
    product: 0

  return Object.assign(

const al = Employee('Al');;
console.log(al.product); // 2


Object composition in Vanilla JavaScript is anti-fragile. I don’t have to keep changing my code when the language’s class API surface shifts. I don’t have to get things working again when packages in TypeScript’s Node ecosystem deliver breaking changes in exchange for fancier features or security enhancements. (This is not an anti-security statement).

Keep The Web Simple

I often wonder how many frontend engineers learn frameworks, libraries and supersets, yet never realize the awesome power of modern JavaScript.

I love writing pure, enclosed objects, wrapped in the lexical scopes of first class functions, all the way down; There’s Very little magic, and a whole lot of beauty.

If you want learn more about the inner workings of the code patterns above, read Kyle Simpson’s excellent book series called, You Don’t Know JS (Yet).

The following three books are particularly helpful:

  1. Scopes and Closures
  2. This and Object Prototypes
  3. ES6 & Beyond

You Don't Know JavaScript - Book Set

       ___ _ _  ____________
 ___  | __/ | ||_   ___   _ \
/___/ | _|| | |__| | |_ \   /
      |_| |_|____|_||___/_|_\