Robouste
Robouste

Reputation: 3680

Javascript/Typescript - Extend Date object with new method

I'm trying to extend the Date object to add useful methods.

According to W3Schools, this should work:

Date.prototype.removeTime = () => {
    // some awesome code to remove the timestamp
}

But Typescript complains that removeTime is not a known property of the Date object. (It is technically right)

So, I tried this:

Object.defineProperty(Date.prototype, "removeTime", {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function() {
        // Do some magic
        return null;
    }
});

But again, when using it in the code:

Property 'removeTime' does not exist on type 'Date'

I don't understand because I did that to add a method to the array prototype, like this:

Object.defineProperty(Array.prototype, "unique", {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function() {
        const a = this.concat();
        for (let i = 0; i < a.length; ++i) {
            for (let j = i + 1; j < a.length; ++j) {
                if (a[i] === a[j]) {
                    a.splice(j--, 1);
                }
            }
        }

        return a;
    }
});

And this works, I can use it in the code and Typescript doesn't complain.

What am I missing here ? Why does it work for Array and not with Date ?

EDIT: After reading the comments, I decided to go with a method approach:

removeTimeFromDate(date: Date): Date {
    return new Date(date.getFullYear(), date.getMonth(), date.getDay());
}

Upvotes: 1

Views: 1606

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1075129

There are two different things here:

  1. Adding the runtime method

  2. Adding the type information

What you're doing is adding the runtime thing, but you're not updating the type.

You can add the type information like this:

interface Date {
    removeTime(): Date;
}

That adds to the Date interface.

(Also, when adding the runtime method, use the defineProperty approach, not the assignment with arrow function.)

Here's a full example:

// Type
interface Date {
    removeTime(): Date;
}

// Runtime
Object.defineProperty(Date.prototype, "removeTime", {
    enumerable: false,   // This is the default, you can leave it off if you like
    configurable: true,  // Normally, method properties are configurable
    writable: true,      // Normally, method properties are writable
    value: function() {
        // Do some magic
        this.setHours(0, 0, 0, 0);
        return this;
    }
});

let d = new Date();
console.log(d.toISOString());
d.removeTime();
console.log(d.toISOString());

On the playground


FWIW, a couple of notes on extending builtins:

  • It's generally not best practice in code that will be used in projects you don't directly control (library code you're writing to be used in other people's projects, etc.).
  • It's generally best practice to use defineProperty to define a non-enumerable property (usually writable and configurable, though).
  • Pick names that are really specific to your project, so that if the builtin is added to later in the specification, your name is unlikely to happen to get used. It's just possible a removeTime method might be added to Date at some point (though it's unlikely), but there's no chance an myRemoveTime method will be.

Upvotes: 3

Related Questions