Last updated: Feb 27, 2024
Reading timeยท6 min
To extend String.prototype in TypeScript:
string.extensions.ts
file.String
interface and add the extension method.import './string.extensions'
before using
it.Here is the content of 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; };
And here is how we import and use the new prefix
method on the String
prototype:
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
, 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.
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:
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.
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.
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.
interface String { toLowerCase(): string; } String.prototype.toLowerCase = function () { return this.toUpperCase(); };
And here is the code in our index.ts
file.
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.
To extend Object.prototype in TypeScript:
object.extensions.ts
file.Object
interface and add the extension method.import './object.extensions'
before using
it.Here is the content of 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>; };
And here is how we import and use the new log
method on the Object
prototype:
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
, 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.
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:
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.
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.
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:
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.
interface Object { toString(): string; } Object.prototype.toString = function () { return 'hello'; };
And here is the code in our index.ts
file.
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.
To extend Date.prototype in TypeScript:
date.extensions.ts
file.Date
interface and add the extension method.import './date.extensions'
before using it.Here is the content of date.extensions.ts
:
// eslint-disable-next-line @typescript-eslint/no-unused-vars interface Date { getTimestamp(): number; } Date.prototype.getTimestamp = function () { return this.getTime(); };
And here is how we import and use the new getTimestamp
method on the
Date prototype:
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
, 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.
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:
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.
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.
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.
interface Date { getFullYear(): number; } Date.prototype.getFullYear = function () { return 100; };
And here is the code in our index.ts
file.
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.
To extend Number.prototype in TypeScript:
number.extensions.ts
file.Number
interface and add the extension method.import './number.extensions'
before using
it.Here is the content of 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; };
And here is how we import and use the new sum
method on the Number
prototype:
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
, 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.
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:
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.
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.
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:
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.
interface Number { toString(): string; } Number.prototype.toString = function () { return 'hello world'; };
And here is the code in our index.ts
file.
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.
You can learn more about the related topics by checking out the following tutorials: