Назад | Учебник TypeScript | Вперёд
Обобщения или generics в TypeScript имеют определённое сходство с обобщениями в Java.
Посмотрите на код ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Lair { private _inhabitant: any; get inhabitant() : any { return this._inhabitant; } set inhabitant(newInhabitant : any) { this._inhabitant = newInhabitant; } } let lair:Lair = new Lair(); lair.inhabitant = 234; alert(lair.inhabitant); lair.inhabitant = "vasya"; alert(lair.inhabitant); |
В нашем Lair может жить inhabitant любого типа, так как мы указали тип any. Проблема в том, что мы не знаем, какого типа inhabitant живёт в нашем Lair в текущий момент. Мы можем проверить тип на этапе выполнения, но на этапе компиляции информация о типе отсутствует. Мы можем присвоить значение любого типа переменной inhabitant, а значит, мы должны ожидать значение любого типа, когда мы считываем значение из inhabitant.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Lair<T> { private _inhabitant: T; get inhabitant() : T { return this._inhabitant; } set inhabitant(newInhabitant : T) { this._inhabitant = newInhabitant; } } let lair = new Lair<string>(); lair.inhabitant = "vasya"; // Нельзя. number is incompatible with string. // lair.inhabitant = 234; alert(lair.inhabitant); |
Обобщёнными могут быть не только классы, но и обычные функции:
1 2 3 4 5 6 7 8 |
function someFunction<T>(arg0 : T) { alert(arg0); } someFunction<string>("Vasya"); // Compile time error. Несовместимый тип аргумента. // someFunction<number>("Vasya"); |
Иногда бывает нужно использовать обобщённый тип, но при этом заранее известно, что этот параметр типа будет дочерним к какому-то определённому типу, метод которого нам нужно использовать. В этом случае мы можем использовать ограничения:
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 31 32 33 34 35 36 37 38 39 40 41 42 |
class Monster{ protected health:number; constructor(health: number) { this.health = health; } hit(damage: number):void { } } class Goblin extends Monster { protected gold: number; } class Beast extends Monster { protected fearRange: number; } function process<T extends Monster>(m : T): void { m.hit(100); } let g:Goblin = new Goblin(34); let b:Beast = new Beast(90); let m:Monster = new Monster(50); process(g); process(b); process(m); class Person { protected name:string; } let p:Person = new Person(); // Нельзя, так как мы ограничили наследниками Monster // Будет ошибка компиляции // process(p); |
Обратите внимание на то, как мы объявили функцию process:
1 2 3 |
function process<T extends Monster>(m : T): void { m.hit(100); } |
Мы указали T extends Monster, чтобы ограничить параметр типа T только классом Monster и его наследниками. Теперь при компиляции будет проверяться, что передаваемый аргумент является экземпляром класса Monster или его наследником.
Можно создавать фабричные методы, принимающие параметр типа в качестве аргумента:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function create<T>(c: { new(): T; }): T { return new c(); } class Monster{ private health: number; public getName(): string { return "Goblin"; } } let obj = create(Monster); alert(obj.getName()); // выведет Goblin |
TypeScript позволяет задать ограничение одного параметра типа на основе другого параметра типа. Например, в примере выше мы указываем, что параметр типа K должен быть существующем полем в параметре типа T:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function getProperty<T, K extends keyof T>(obj: T, key: K) { return obj[key]; } let monster = { health: 100, ammo: 56 }; alert(getProperty(monster, "ammo")); // okay // Ошибка компиляции. В Monster нет поля armor. //alert(getProperty(monster, "armor")); |
В принципе, написанного мной по TypeScript уже должно быть достаточно для написания простейших программ.
Назад | Учебник TypeScript | Вперёд