Skip to main content

Command Palette

Search for a command to run...

When clean API design meets deep JavaScript internals. 🚀

Updated
•2 min read
When clean API design meets deep JavaScript internals. 🚀
B

Backend-focused Software Engineer skilled in Node.js, Go, TypeScript, and scalable API design, dedicated to building high-performance, reliable, and efficient web systems.

We often rely on syntactic sugar in modern development, but there is immense value in understanding the raw mechanics of JavaScript—specifically how Prototypes, Context (this), and Fluent Interfaces work together.

I was experimenting with a pattern today that combines these concepts to create a readable, chainable API.

Why is this snippet interesting?

  1. Fluent Interface (Method Chaining): By returning this at the end of sayHello, we enable endless chaining: .sayHello().sayHello().multiply(). This makes the code read like a sentence, vastly improving Developer Experience (DX).

  2. Prototype vs. Own Properties (Shadowing): The code explicitly distinguishes between the instance (Rony) and the prototype (Borhan).

    • obj.hasOwnProperty("name") returns true because "Rony" is on the instance.

    • However, we can still access the "parent" data via the prototype chain.

  3. Generics & Binding: Using TypeScript generics <T extends Class> combined with .bind, we create a strictly typed factory function that pre-fills arguments ("Khulna", "Bangladesh") while maintaining type safety.

It’s a great exercise to remember that under every fancy TypeScript class, there is a powerful (and sometimes complex) prototype chain doing the heavy lifting. 🛠️

Here is the code structure:

type Class = new () => any;
type Person = {
  name: string;
  sayHello: () => Person;
  multiply: () => void;
};

function Person(this: Person) {
  this.name = "Rony"; // Instance Property

  this.sayHello = function () {
    console.log(`hello ${this.name}`);
    return this; // Enables Chaining
  };
  this.multiply = function () {
    console.log("Multiplication");
  };
}

// Shared Prototype Data
Person.prototype = {
  name: "Borhan", 
};

const obj = {
  test<T extends Class, U extends any[]>(this: T, country: U) {
    const obj = new this() as Object;

    // Proving "Rony" is an Own Property
    console.log("TCL: obj.hasOwnProperty()", obj.hasOwnProperty("name"));

    // Accessing the Prototype (Borhan)
    const prototype = Object.getPrototypeOf(obj);
    console.log(`${prototype.name} is from ${country[0]},${country[1]}`);

    return obj as () => T;
  },
};

const getInfo = obj.test.bind(Person, ["Khulna", "Bangladesh"]);

// The Result: A clean, fluent API
getInfo() 
  .sayHello() 
  .sayHello() 
  .multiply();

More from this blog

Codernex: Exploring Tech & Programming

9 posts

A platform where I share insights, tutorials, and expertise across a range of technologies, helping others stay informed and grow their skills.