Как известно, в JavaScript отсутствует статическая типизация. Поэтому в переменную или функцию может быть передано значение любого типа. Иногда этим пользуются разработчики, создавая аналог перегрузки методов. Например, в качестве параметра может использоваться или строка, или число или массив строк. Давайте разберемся как написать аналогичный код на TypeScript и, по возможности, со статической типизацией. |
Самое простое, но неудобное решение
Самый простой способ – использовать тип any, позволяющий присваивать переменной значения любого типа. В этом случае статической типизации, а значит и отличий от JavaScript, не будет. Даже если разработчик будет знать точный перечень допустимых для присвоения типов. Останется только делать проверки, выполняющиеся во время выполнения приложения.
Объединенные типы
Однако, начиная с TypeScript 1.4 в язык была введена поддержка объединённых типов (union types). В отличии от any, это позволяет ограничить перечень используемых типов и, как следствие, задействовать статическую типизацию. Объявление переменной в этом случае выглядит следующим образом:
var varName: type1 | type2 | ... | typeN;
, где type1…typeN – имена типов.
Разберем использование на примере. Создадим переменную, которая сможет принимать только значения типов string или string[]:
var msg: string | string[];
msg = "Union Types";
msg = ["Union", "Types"];
msg = true; // Ошибка: тип boolean нельзя привести к string или string[]
msg = [42]; // Ошибка: тип int[] нельзя привести к string или string[]
Строки 4 и 5 не скомпилируются из-за ошибки, т.к. boolean и number[] нельзя привести к указанным в объявлении типам.
Рассмотрим пример простой функции в которую можно передать число, строку или массив строк:
function DisplayMessage(msg: number|string|string[]) : void {
if (typeof msg === "number") {
alert("Число: " + msg);
} else if (typeof msg === "string") {
alert("Сообщение: " + msg);
} else {
msg.forEach(function (m, i) {
alert(i + ": " + m);
});
}
}
DisplayMessage(42);
DisplayMessage("Hellow World");
DisplayMessage(["Hellow", "World"]);
// Некорректные вызовы:
// DisplayMessage(new Object());
// DisplayMessage([42, 24]);
Вывод конкретных типов из объединенных
Говоря об объединенных типах, необходимо отметить один важный и интересный момент. Компилятор TypeScript понимает конструкции if/else c typeof или instanceof и внутри их блоков рассматривает указанную переменную как имеющую уже конкретный тип. Например, следующий код вызовет ошибку компиляции:
function DisplayMessage(msg: number|string|string[]): void {
var str = msg.toString();
if (typeof msg === "number") {
var fixed1 = msg.toFixed(2);
alert("Число: " + msg);
} else if (typeof msg === "string") {
var fixed2 = msg.toFixed(2);
alert("Сообщение: " + msg);
} else {
msg.forEach(function (m, i) {
alert(i + ": " + m);
});
}
}
Проблема в строке 7, где тип msg будет равен string, у которого нет метода toFixed(). При этом аналогичный код в строке 4 корректен, т.к. там msg имеет тип number. В строке 11 тип переменной msg выводится (в блоке else) как оставшийся из всех – string[].
Так же обратите внимание на строку 2. Здесь конкретный тип msg еще не известен. Однако, ошибки не будет, т.к. метод toString() присутствует у всех перечисленных типов.