简体   繁体   中英

TypeScript interface signature “(): string”

In types definitions from a standard Node.js library, I found a definition of the interface DateConstructor .

interface DateConstructor {
    new(): Date;
    new(value: number): Date;
    new(value: string): Date;
    new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
    (): string;
    readonly prototype: Date;
    /**
      * Parses a string containing a date, and returns the number of milliseconds between that date and midnight, January 1, 1970.
      * @param s A date string
      */
    parse(s: string): number;
    /**
      * Returns the number of milliseconds between midnight, January 1, 1970 Universal Coordinated Time (UTC) (or GMT) and the specified date.
      * @param year The full year designation is required for cross-century date accuracy. If year is between 0 and 99 is used, then year is assumed to be 1900 + year.
      * @param month The month as an number between 0 and 11 (January to December).
      * @param date The date as an number between 1 and 31.
      * @param hours Must be supplied if minutes is supplied. An number from 0 to 23 (midnight to 11pm) that specifies the hour.
      * @param minutes Must be supplied if seconds is supplied. An number from 0 to 59 that specifies the minutes.
      * @param seconds Must be supplied if milliseconds is supplied. An number from 0 to 59 that specifies the seconds.
      * @param ms An number from 0 to 999 that specifies the milliseconds.
      */
    UTC(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): number;
    now(): number;
}

declare const Date: DateConstructor;

It contains strange definition (): string . How can I define it in class, that wants to implement this interface?

In case you think this question is different enough from the other one I linked to :

That definition means the class constructor is also a callable function with no arguments that returns a string when called without new . You can't use a ES2015 -or- later class and adhere to the specification, since it would need to throw a TypeError upon being called without new . Instead you can return a function that detects being called with new , with extra properties added to implement static methods.

I'll give you an example where I provide a wrapper around the built-in Date constructor object. First let's describe the part of the interface that acts like a function, either with or without the new keyword:

interface FunctionalPartOfDateConstructor {
  new(): Date;
  new(value: number): Date;
  new(value: string): Date;
  new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
  (): string;
}

Now let's try to implement just that part:

const funcPart = function(valueOrYear?: number | string, month?: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date | string {
  if (typeof new.target === 'undefined') {
    // called as function
    return Date();
  }
  if (typeof valueOrYear === 'undefined') {
    // called as constructor with no arguments
    return new Date();
  }
  if (typeof valueOrYear === 'string') {
    // called as constructor with string value argument
    return new Date(valueOrYear);
  }
  if (typeof month === 'undefined') {
    // called as constructor with number value argument
    return new Date(valueOrYear);
  }
  // called as constructor with year, month, date, etc arguments:
  return new Date(valueOrYear, month, date, hours, minutes, seconds, ms);
} as FunctionalPartOfDateConstructor;

Note that I use new.target to detect if the function is called with new . This compiles to something reasonable when targeting ES5, I think. And note that it has to tease apart the difference between all the different overload signatures.

Now we can make the full DateConstructor instance by merging the functional part with something that implements static methods:

const myDateConstructor: DateConstructor = Object.assign(funcPart, {
  prototype: Date.prototype,
  parse(s: string) {
    return Date.parse(s);
  },
  UTC(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number) {
    return Date.UTC(year, month, date, hours, minutes, seconds, ms);
  },
  now() {
    return Date.now();
  }
})

You can try it on the TypeScript Playground if you want. Hope that helps; good luck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM