javascripttypescriptdatetimeconstructor-overloading

How to define the constructor for a class that extends Date?


How can I correctly define the constructor for a child-class of Date (typescript 4.1.3)?

The constructor definition of the Date object is this:

new(): Date;
new(value: number | string): Date;
new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;

in my code, I try to specify this:

class MyDate extends Date {
    // overloads copied from DateConstructor
    constructor();
    constructor(value: number | string);
    constructor(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number);
    // constructor impl
    constructor(
        yearOrValue?: number | string, month?: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number
    ) {
        super(yearOrValue, month, date, hours, minutes, seconds, ms);
        /*    ^^^^^^^^^^^
              Argument of type 'string | number | undefined' is not assignable to parameter of type 'number'.
              Type 'undefined' is not assignable to type 'number'.(2345)
         */         
    }
}

but this causes a compile error in the super call:

Argument of type 'string | number | undefined' is not assignable to parameter of type 'number'.
Type 'undefined' is not assignable to type 'number'.(2345)

How can I define the constructor parameters, so that I can call super with all possibilities that the original Date object has and keep type-safety (as much as possible)?

Notes:


Solution

  • If you want to support all of the signatures that Date supports,¹ I don't think you have much choice other than branching (or punting):

    class MyDate extends Date {
        // overloads copied from DateConstructor
        constructor();
        constructor(value: number | string);
        constructor(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number);
        // constructor impl
        constructor(
            yearOrValue?: number | string,
            month?: number,
            date?: number,
            hours?: number,
            minutes?: number,
            seconds?: number,
            ms?: number
        ) {
            if (typeof yearOrValue === "undefined") {
                super();
            } else if (typeof month === "undefined") {
                super(yearOrValue);
            } else {
                // consider an assertion here that `yearOrValue` is a number
                super(yearOrValue as number, month, date, hours, minutes, seconds, ms);
            }
        }
    }
    

    Playground link


    ¹ (For what it's worth, I don't think you have to, though you may well want to. Unlike Array or Promise, I don't think the Date constructor is called anywhere in the standard library)