Prototype in Javascript | Codementor

TypeScript is a supercharged version of JavaScript that provides static typing and powerful features like interfaces and type augmentation. One of the cool features of TypeScript is the ability to extend built-in prototypes (like String, Array, Number) to add custom methods that can be used globally across your project.

In this blog post, we’ll build a TypeScript utility library that adds custom methods to native prototypes such as String, Array, and Number. We’ll also learn how to extend global types using the declare keyword, enabling us to use the extended methods throughout the project.

Why Extend Prototypes in TypeScript?

In JavaScript, native types like String, Array, and Number come with a basic set of methods. However, you might want to add some additional functionality to these objects for your specific use case. For example:

  • Converting strings to Title Case.
  • Getting the sum of an array of numbers.
  • Formatting a number as currency.

These enhancements are useful for improving code readability and maintainability, especially in large-scale applications.

Setting Up the TypeScript Project

First, let’s initialize a new TypeScript project and set up the environment:

  1. Initialize the project

    mkdir ts-utils
    cd ts-utils
    npm init -y
    
  2. Install TypeScript

    npm install typescript --save-dev
    
  3. Create a tsconfig.json file
    This configuration file tells TypeScript how to compile the project.

    {
      "compilerOptions": {
        "target": "ES6",
        "module": "CommonJS",
        "strict": true,
        "outDir": "dist"
      }
    }
    
  4. Create a src folder for our source code

    mkdir src
    

Extending Prototypes

Now, let’s extend the prototypes of String, Array, and Number to add some useful utility methods.

1. Extending String Prototype with toTitleCase()

We want to add a method called toTitleCase() that converts a string into Title Case (e.g., “hello world” becomes “Hello World”).

// src/string-utils.ts
export {};

declare global {
  interface String {
    toTitleCase(): string;
  }
}

// Adding method to String prototype
String.prototype.toTitleCase = function (): string {
  return this.replace(/\b\w/g, (char) => char.toUpperCase());
};

In this code:

  • declare global tells TypeScript that we’re modifying the global Stringtype.
  • The toTitleCase() method is added to the String prototype, which allows us to use this method on any string in our project.

2. Extending Array Prototype with sum()

Next, let’s add a method called sum() to the Array prototype that calculates the sum of all numeric elements in an array.

// src/array-utils.ts
export {};

declare global {
  interface Array<T> {
    sum(): number;
  }
}

// Adding method to Array prototype
Array.prototype.sum = function (): number {
  return this.reduce((acc, num) => acc + (typeof num === "number" ? num : 0), 0);
};

Here:

  • We declare the sum() method on the Array prototype to add the capability of calculating the sum of numeric values in an array.

3. Extending Number Prototype with toCurrency()

Finally, let’s create a method called toCurrency() for the Number prototype to format a number as a currency.

// src/number-utils.ts
export {};

declare global {
  interface Number {
    toCurrency(locale?: string, currency?: string): string;
  }
}

// Adding method to Number prototype
Number.prototype.toCurrency = function (locale = "en-US", currency = "USD"): string {
  return new Intl.NumberFormat(locale, { style: "currency", currency }).format(this);
};

In this snippet:

  • The toCurrency() method formats a number as a currency string based on the provided locale and currency options.

Making Sure TypeScript Knows About Our New Methods

We’ve successfully added new methods to the native prototypes, but we need TypeScript to recognize these additions. This is where the declare keyword comes in.

By using declare global, we tell TypeScript that we’re adding new methods to global types. This will make TypeScript aware of the new methods and help provide autocomplete and type checking in the rest of the project.

Creating the Main Index File

To ensure all these methods are available globally when we import the library, we need an index.ts file that imports all the utility files.

// src/index.ts
import "./string-utils";
import "./array-utils";
import "./number-utils";

Testing the Utility Library

Now, let’s create a test file (test.ts) to check if our utility functions are working as expected.

// test.ts
import "./src"; // Import utility methods

console.log("hello world".toTitleCase());  // "Hello World"
console.log([10, 20, 30].sum());           // 60
console.log((1234.56).toCurrency("en-US", "USD"));  // "$1,234.56"
console.log((1234.56).toCurrency("en-IN", "INR"));  // "₹1,234.56"

Building and Running the Project

Before running the test, let’s add a build script to package.json.

"scripts": {
  "build": "tsc"
}

Then, build and run the project:

  1. Build the TypeScript code

    npm run build
    
  2. Run the test file
    Use ts-node to run the test.ts file:

    npx ts-node test.ts
    

You should see the following output:

Hello World
60
$1,234.56
₹1,234.56

Publishing to NPM

Finally, let’s publish this as an NPM package:

  1. Login to NPM

    npm login
    
  2. Update the package.json file with necessary details like name, version, main, and types:

{
  "name": "ts-utils",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc"
  }
}
  1. Publish the package
    npm run build
    npm publish
    

Using the Library in Another Project

Now, you can use your utility library in other TypeScript projects by simply installing it from NPM:

npm install ts-utils

And use it like this:

import "ts-utils";

console.log("typescript rocks".toTitleCase());  // "Typescript Rocks"
console.log([1, 2, 3, 4].sum());  // 10
console.log((99.99).toCurrency("en-GB", "GBP"));  // "£99.99"

Conclusion

In this blog, we’ve learned how to extend built-in prototypes in TypeScript using the declare global syntax. We created a small utility library that added helpful methods to the String, Array, and Number types, and made sure TypeScript recognized these extensions for type checking and autocompletion.

This technique can be used to create reusable libraries that improve developer productivity and code readability. You can share or publish these libraries for use in other projects to make your life easier!

Happy coding! ✨