#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 .