Перегрузить определение типа класса в Typescript

#typescript #typescript-typings

#typescript #typescript-типизации

Вопрос:

Я хотел бы перегрузить определение класса в TypeScript таким образом, чтобы при создании экземпляра этого класса со значением, переданным конструктору, определенное свойство вводилось с помощью предоставленной переменной типа (скажем number ). Когда экземпляр класса создается без значения, переданного конструктору, вводится то же свойство, что и: предоставленная переменная типа или неопределенная (ie number | undefined ).

Пример:

 const car1 = new Car<number>(1);
car1.maxSpeed                 // maxSpeed is of type number

const car2 = new Car<number>();
car1.maxSpeed                 // maxSpeed is of type number | undefined
  

Как это можно сделать?

Комментарии:

1. Я обновил свой ответ, может быть, это поможет?

Ответ №1:

Вы, конечно, можете описать и объявить конструктор класса, который работает таким образом:

 interface CarConstructor {
    new <T>(): Car<T | undefined>;
    new <T>(maxSpeed: T): Car<T>;
}
interface Car<T> {
    maxSpeed: T
}

declare const Car: CarConstructor;

const car1 = new Car<number>(1);
car1.maxSpeed // maxSpeed is of type number

const car2 = new Car<number>();
car2.maxSpeed // maxSpeed is of type number | undefined
  

A CarConstructor имеет две сигнатуры конструкции, перегруженные так, как вы говорите.


Проблема заключается в реализации такого CarConstructor . TypeScript не позволяет вам указывать параметры типа в class конструкторах, поскольку в настоящее время компилятор получает любые параметры универсального типа класса из самого объявления класса, а не из конструкторов:

 class BadCar<T> {
    maxSpeed: T;
    constructor<T>(): Car<T | undefined>; // error! no type params
    constructor<T>(maxSpeed: T): Car<T>; // error! no type params
    constructor(maxSpeed?: T) {
        this.maxSpeed = maxSpeed; // error! can't tell this is valid
    }
}
  

Прямо сейчас все, что вы можете сделать, это создать класс, который «достаточно близок», а затем аннотировать или утверждать, что это CarConstructor :

 const Car: CarConstructor = class <T> {
    maxSpeed: T;
    constructor(maxSpeed?: T) {
        this.maxSpeed = maxSpeed as T; // <-- assume undefined is in T 
    }
}
  

Это работает, но это громоздко и, вероятно, становится более громоздким, чем больше функциональности существует Car (вы обнаружите, что повсюду делаете избыточные определения свойств / методов).


Существует существующая проблема GitHub, microsoft / TypeScript #35387, требующая, чтобы было проще напрямую реализовывать классы с перегруженными конструкторами такого рода. Если вам это действительно нужно, вы можете перейти к этой проблеме и поставить ей 👍 и описать, почему ваш вариант использования является убедительным.


Конечно, проще всего не бороться с TypeScript и использовать другое решение, например, фабричную функцию:

 class DumbCar<T> {
    constructor(public maxSpeed: T) { }
}

function newCar<T>(x: T): Car<T>;
function newCar<T>(): Car<T | undefined>;
function newCar<T>(x?: T): Car<T | undefined> | Car<T> {
    return new DumbCar(x);
}
  

Ссылка на игровую площадку для кода

Ответ №2:

В общем случае это невозможно, если вы попытаетесь аннотировать конструктор, вы получите:

Аннотация типа не может отображаться в объявлении конструктора. ts(1093)

В этом случае вам, вероятно, следует просто определить заводскую функцию вместо этого.


Если тип указан явно, вы можете просто указать его как таковой:

 class Car<T>
{
    maxSpeed: T;
    // ...

    constructor();
    constructor(maxSpeed: T);
    constructor(maxSpeed?: any)
    {
        this.maxSpeed = maxSpeed;
    }
}

const car1 = new Car<number | undefined>();
const car2 = new Car<number>(1);
  

Обратите внимание, что для этого требуется параметр компилятора strict null checks .