Помню, я когда-то описывал type inference или вывод типов в Java. В TypeScript тоже есть нечто подобное
1 |
let v1 = "Monster"; |
Если переменной присваивается массив, то типом переменной будет массив из элементов наиболее общего типа из всех перечисленных при инициализации:
1 2 3 4 5 6 7 8 9 10 11 12 |
let vvv = ["Monster", "Goblin", null]; // Тип vvv будет выведен автоматически как string[] vvv[0] = null; vvv[0] = "Daemon"; vvv[0] = 1; // Ошибка. Нельзя присвоить 1 элементу массива string[] class Monster{ } vvv[0] = new Monster(); // Ошибка. Нельзя присвоить Monster элементу массива string[] |
Причём типом элементов массива становится наиболее общий тип из ПРИСУТСТВУЮЩИХ элементов в блоке инициализации. Если наиболее общий тип существует, но его нет в блоке инициализации, то он не выводится:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Monster{ health: number; } class Goblin extends Monster { goblinField : string; } class Daemon extends Monster { daemonField : string; } class Kakodaemon extends Monster { kakodaemonField : string; } class Ghoul extends Monster { ghoulField: string; } let monsters = [new Goblin(), new Daemon(), new Kakodaemon()]; monsters[0] = new Ghoul(); // Нельзя, так как тип monsters будет // Объединённым типом // (Goblin | Daemon | Kakodaemon)[] // А не типом Monster[] |
В этом случае лучше явно указать тип массива:
1 2 3 |
let monsters: Monster[] = [new Goblin(), new Daemon(), new Kakodaemon()]; monsters[0] = new Ghoul(); // OK |
Но обратите внимание на одну особенность TypeScript: Если в случае с объединённым типом (Goblin | Daemon | Kakodaemon)[] в Ghoul были бы необходимые поля для Goblin или Daemon или Kakodaemon, то значение Ghoul можно было бы записать в этот массив:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class Monster{ health: number; } class Goblin extends Monster { goblinField : string; } class Daemon extends Monster { daemonField : string; } class Kakodaemon extends Monster { kakodaemonField : string; } class Ghoul extends Monster { daemonField : string; // Обратите внимание, // что теперь // у нас есть поле, необходимое // для того, чтобы Ghoul был // совместим с Daemon. ghoulField : string; // Просто дополнительное поле } let monsters = [new Goblin(), new Daemon(), new Kakodaemon()]; monsters[0] = new Ghoul(); // OK, так как Ghoul совместим с Daemon |
Вывод типа работает и в обратную сторону, когда тип выражения выводится на основе типа переменной, которой присваивается это значение. Пример:
1 2 3 4 5 6 7 8 |
window.onmousedown = function(mouseEvent) { console.log(mouseEvent.button); //<- OK console.log(mouseEvent.stupidField); //<- Ошибка, TypeScript // знает, что у параметра onmousedown // (который типа MouseEvent) // нет поля stupidField }; |