Extend String.prototype and other prototypes in TypeScript

avatar
Borislav Hadzhiev

Last updated: Feb 27, 2024
6 min

banner

# Table of Contents

  1. Extend String.prototype in TypeScript
  2. Extend Object.prototype in TypeScript
  3. Extend Date.prototype in TypeScript
  4. Extend Number.prototype in TypeScript

# Extend String.prototype in TypeScript

To extend String.prototype in TypeScript:

  1. Create a string.extensions.ts file.
  2. Extend the String interface and add the extension method.
  3. Import the extension method as import './string.extensions' before using it.

Here is the content of string.extensions.ts:

string.extensions.ts
// eslint-disable-next-line @typescript-eslint/no-unused-vars interface String { prefix(pre: string): string; } String.prototype.prefix = function (pre: string) { return pre + this; };
The code for this article is available on GitHub

And here is how we import and use the new prefix method on the String prototype:

index.ts
import './string.extensions'; const str = 'world'; console.log(str.prefix('hello ')); // ๐Ÿ‘‰๏ธ "hello world"

If I run my index.ts file, we can see that the prefix method gets successfully invoked.

string prototype extended

We declared a new interface String, which will get merged with the original String interface.

In the interface, we created a prefix method, which adds leading characters to the original string and returns the result.

Make sure to use a named function when extending the prototype, and not an arrow function, because arrow functions use the this of the enclosing scope, which is not what you want.

You could use this approach to extend String.prototype with any method. Here is an example of a method that adds the passed-in strings together.

This is the code in the string.extensions.ts file:

string.extensions.ts
interface String { add(...strings: string[]): string; } String.prototype.add = function (...strings) { return this + strings.join(''); };

And here is the index.ts file, which imports string.extensions and makes use of the add method.

index.ts
import './string.extensions'; const str = 'hello'; // ๐Ÿ‘‡๏ธ "hello one two three" console.log(str.add(' one', ' two', ' three'));

Make sure to specify the correct path when importing the string.extensions.ts module.

When overriding String.prototype, make sure your methods are not interfering with built-in method names unless intentionally overriding them (which can be confusing).

Here is an example of how you would override the built-in toLowerCase() method.

string.extensions.ts
interface String { toLowerCase(): string; } String.prototype.toLowerCase = function () { return this.toUpperCase(); };

And here is the code in our index.ts file.

index.ts
import './string.extensions'; const str = 'Hello World'; // ๐Ÿ‘‡๏ธ "HELLO WORLD" console.log(str.toLowerCase());

Note that overriding built-in methods is confusing and should generally be avoided.

I've also written an article on how to extend Array.prototype in TS.

# Table of Contents

  1. Extend Object.prototype in TypeScript
  2. Extend Date.prototype in TypeScript
  3. Extend Number.prototype in TypeScript

# Extend Object.prototype in TypeScript

To extend Object.prototype in TypeScript:

  1. Create an object.extensions.ts file.
  2. Extend the Object interface and add the extension method.
  3. Import the extension method as import './object.extensions' before using it.

Here is the content of object.extensions.ts:

object.extensions.ts
// eslint-disable-next-line @typescript-eslint/no-unused-vars interface Object { // ๐Ÿ‘‡๏ธ function log - no parameters, returns object log(): Record<string, unknown>; } // ๐Ÿ‘‡๏ธ Don't use an arrow function Object.prototype.log = function () { console.log(this); return this as Record<string, unknown>; };
The code for this article is available on GitHub

And here is how we import and use the new log method on the Object prototype:

index.ts
import './object.extensions'; const obj = { name: 'Tom', age: 30, }; obj.log();

If I run my index.ts file, we can see that the log method gets successfully invoked.

object prototype extended

We declared a new interface Object, which will get merged with the original Object interface.

In the interface, we created a log method, which returns an object with string keys and unknown values.

The method simply logs the object and returns it.

Make sure to use a named function when extending the prototype, and not an arrow function, because arrow functions use the this of the enclosing scope, which is not what you want.

You could use this approach to extend Object.prototype with any method. Here is an example that implements a merge method that merges 2 objects.

This is the code in the object.extensions.ts file:

object.extensions.ts
interface Object { merge(obj: Record<string, unknown>): Record<string, unknown>; } Object.prototype.merge = function (obj: Record<string, unknown>) { return { ...this, ...obj }; };

And here is the index.ts file, which imports object.extensions and makes use of the merge method.

index.ts
import './object.extensions'; const obj = { name: 'Tom', age: 30, }; const merged = { country: 'Chile', city: 'Santiago' }.merge(obj); // ๐Ÿ‘‡๏ธ {country: 'Chile', city: 'Santiago', name: 'Tom', age: 30} console.log(merged);

Make sure to specify the correct path when importing the object.extensions.ts module.

When overriding Object.prototype, make sure your methods are not interfering with built-in method names unless intentionally overriding them (which can be confusing).

Here is the original Object interface:

index.ts
interface Object { constructor: Function; toString(): string; toLocaleString(): string; valueOf(): Object; hasOwnProperty(v: PropertyKey): boolean; isPrototypeOf(v: Object): boolean; propertyIsEnumerable(v: PropertyKey): boolean; }

For example, here is how you would override the built-in toString() method.

object.extensions.ts
interface Object { toString(): string; } Object.prototype.toString = function () { return 'hello'; };

And here is the code in our index.ts file.

index.ts
import './object.extensions'; const obj = { name: 'Tom', age: 30, }; console.log(obj.toString()); // ๐Ÿ‘‰๏ธ "hello"

Note that overriding built-in methods is confusing and should generally be avoided.

# Table of Contents

  1. Extend Date.prototype in TypeScript
  2. Extend Number.prototype in TypeScript

# Extend Date.prototype in TypeScript

To extend Date.prototype in TypeScript:

  1. Create a date.extensions.ts file.
  2. Extend the Date interface and add the extension method.
  3. Import the extension method as import './date.extensions' before using it.

Here is the content of date.extensions.ts:

date.extensions.ts
// eslint-disable-next-line @typescript-eslint/no-unused-vars interface Date { getTimestamp(): number; } Date.prototype.getTimestamp = function () { return this.getTime(); };
The code for this article is available on GitHub

And here is how we import and use the new getTimestamp method on the Date prototype:

index.ts
import './date.extensions'; const date = new Date('2022-09-24'); // ๐Ÿ‘‡๏ธ 1663977600000 console.log(date.getTimestamp());

If I run my index.ts file, we can see that the getTimestamp method gets successfully invoked.

date prototype extended

We declared a new interface Date, which will get merged with the original Date interface.

In the interface, we created a getTimestamp method, which returns the number of milliseconds since the UNIX epoch.

Make sure to use a named function when extending the prototype, and not an arrow function, because arrow functions use the this of the enclosing scope, which is not what you want.

You could use this approach to extend Date.prototype with any method. Here is an example of a method that formats the date as yyyy-mm-dd.

This is the code in the date.extensions.ts file:

date.extensions.ts
interface Date { format(): string; } Date.prototype.format = function () { return formatDate(this); }; function padTo2Digits(num: number) { return num.toString().padStart(2, '0'); } function formatDate(date: Date) { return [ date.getFullYear(), padTo2Digits(date.getMonth() + 1), padTo2Digits(date.getDate()), ].join('-'); }

And here is the index.ts file, which imports date.extensions.ts and makes use of the format method.

index.ts
import './date.extensions'; const date = new Date('2022-09-24'); // ๐Ÿ‘‡๏ธ "2022-09-24" console.log(date.format());

Make sure to specify the correct path when importing the date.extensions.ts module.

When overriding Date.prototype, make sure your methods are not interfering with built-in method names unless intentionally overriding them (which can be confusing).

Here is an example of how you would override the built-in getFullYear() method.

date.extensions.ts
interface Date { getFullYear(): number; } Date.prototype.getFullYear = function () { return 100; };

And here is the code in our index.ts file.

index.ts
import './date.extensions'; const date = new Date('2022-09-24'); // ๐Ÿ‘‡๏ธ 100 console.log(date.getFullYear());

Note that overriding built-in methods is confusing and should generally be avoided.

# Extend Number.prototype in TypeScript

To extend Number.prototype in TypeScript:

  1. Create a number.extensions.ts file.
  2. Extend the Number interface and add the extension method.
  3. Import the extension method as import './number.extensions' before using it.

Here is the content of number.extensions.ts:

number.extensions.ts
// eslint-disable-next-line @typescript-eslint/no-unused-vars interface Number { sum(a: number): number; } Number.prototype.sum = function (a: number) { return Number(this) + a; };
The code for this article is available on GitHub

And here is how we import and use the new sum method on the Number prototype:

index.ts
import './number.extensions'; const num = 100; console.log(num.sum(5000)); // ๐Ÿ‘‰๏ธ 5100

If I run my index.ts file, we can see that the sum method gets successfully invoked.

number prototype extended

We declared a new interface Number, which will get merged with the original Number interface.

In the interface, we created a sum method, which adds 2 numbers and returns the result.

Make sure to use a named function when extending the prototype, and not an arrow function, because arrow functions use the this of the enclosing scope, which is not what you want.

You could use this approach to extend Number.prototype with any method. Here is an example of a method that adds leading zeros to a number.

This is the code in the number.extensions.ts file:

number.extensions.ts
interface Number { addLeadingZeros(): string; } Number.prototype.addLeadingZeros = function () { return padToDigits(Number(this), 5); }; function padToDigits(num: number, totalLength: number) { return num.toString().padStart(totalLength, '0'); }

And here is the index.ts file, which imports number.extensions and makes use of the addLeadingZeros method.

index.ts
import './number.extensions'; const num = 100; // ๐Ÿ‘‡๏ธ "00100" console.log(num.addLeadingZeros());

Make sure to specify the correct path when importing the number.extensions.ts module.

When overriding Number.prototype, make sure your methods are not interfering with built-in method names unless intentionally overriding them (which can be confusing).

This is what the original Number interface looks like:

index.ts
interface Number { toString(radix?: number): string; toFixed(fractionDigits?: number): string; toExponential(fractionDigits?: number): string; toPrecision(precision?: number): string; valueOf(): number; }

Here is an example of how you would override the built-in toString() method.

number.extensions.ts
interface Number { toString(): string; } Number.prototype.toString = function () { return 'hello world'; };

And here is the code in our index.ts file.

index.ts
import './number.extensions'; const num = 100; // ๐Ÿ‘‡๏ธ "hello world" console.log(num.toString());

Note that overriding built-in methods is confusing and should generally be avoided.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev