TmTron
TmTron

Reputation: 19411

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:

Upvotes: 2

Views: 863

Answers (4)

Unlucky
Unlucky

Reputation: 447

I had the same issue and implementing it like this fixed all the TS errors:

class MyDate extends Date {
  constructor();

  constructor(value: number | string);

  constructor(
    year: number,
    month: number,
    date?: number,
    hours?: number,
    minutes?: number,
    seconds?: number,
    ms?: number,
  );

  constructor(
    ...args: [] | [number | string] | [number, number, number?, number?, number?, number?, number?]
  ) {
    if (args.length === 0) {
      super();
    } else if (args.length === 1) {
      super(args[0]);
    } else {
      super(...args);
    }
  }
}

Upvotes: 0

dan-lee
dan-lee

Reputation: 14502

You could do something like this today:

class MyDate extends Date {
  constructor(...args: ConstructorParameters<typeof Date>) {
    super(...args)
  }
}

Upvotes: 2

T.J. Crowder
T.J. Crowder

Reputation: 1075139

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)

Upvotes: 4

gbalduzzi
gbalduzzi

Reputation: 10166

You need to add some check and call the proper parent constructor for each situation:

constructor(
        yearOrValue?: number | string, month?: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number
    ) {
        if (yearOrValue === undefined) {
            super()
        } else if (month === undefined) {
            super(yearOrValue)
        } else if (typeof yearOrValue === 'number') {
            super(yearOrValue, month, date, hours, minutes, seconds, ms)
        }
    }

Working playground link

Upvotes: 2

Related Questions