> For the complete documentation index, see [llms.txt](https://race-timo.gitbook.io/typescript/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://race-timo.gitbook.io/typescript/classes.md).

# Классы

## Немного о классах

Долгое время в JavaScript для создания однотипных объектов использовалась функция-конструктор. Функция-конструктор вызывается при помощи оператора `new`. Эта функция заполняет полями и методами какой-то объект (обычно `this`) и возвращает его (явно или не явно).

```javascript
function Cat(name) {
    this.name = name;
    this.say = function() {
        console.log(this.name, "say:", "meow");
    }
}

const cat1 = new Cat("Tom");
const cat2 = new Cat("Lalaka");
cat1.say();
cat2.say();
```

В ES6 появился синтаксис, который позволяет объявлять классы. По сути класс - это функция-конструктор каких-то объектов с упрощенным синтаксисом наследования, объявления методов и еще некоторыми особенностями. Само понятие класса лучше отражает объектно-ориентированный подход разработки, чем использование функций-конструкторов и прототипов.

```javascript
class Cat {
    constructor(name) {
        this.name = name;
    }

    say() {
        console.log(this.name, "say:", "meow");
    }
}

const cat1 = new Cat("Tom");
const cat2 = new Cat("Lalaka");
cat1.say();
cat2.say();
```

Когда объявляется класс, в память кладется некоторый объект, который умеет создавать новые объекты при помощи оператора `new`. Объект класса - это функция конструктор, в котором хранится логика по заполнению новых других объектов. Объекты, которые были созданы при помощи какого-то класса называются экзэмплярами или инстансами (instance) этого класса.

Важно научится четко различать, что такое "объект класса" и "инстанс класса". Выше сказано, что объект класса - это объект в памяти (это функция). Это означает, что с объектом класса можно работать как с обычным объектам: добавлять поля, присваивать в качестве значения переменной, передавать в параметры функции (и в параметры конструктора другого класса).

```javascript
class Cat {
    constructor(name) {
        this.name = name;
    }
}

const obj = Cat;
const cat1 = new obj("Tom");

function create(classObj) {
    return new classObj("Tom");
}

const cat2 = create(Cat);

Cat.foo = function() {
    console.log("Функция у объекта класса");
}

Cat.foo();
```

Инстансы класса - это самостоятельные объекты, которые были сконструированы этого класса. Они не зависят друг от друга (если это не сделано специально) и с классом их связывает только то, что он являлся их создателем.

Объявление классов в TypeScript имеет ряд особенностей по сравнению с объявлением классов в JavaScript.

## Поля классов

Класс может иметь поля. Для того, чтобы объявить поле, нужно указать его название и тип.

```typescript
class A {
    b: number;
    c: string;
    e: { a: number; b: string };
}
```

Поля могут иметь модификатор доступа. В TypeScript существует 3 модификатора доступа:

* `public` - поле является публичным. Читать и изменять значение этого поля можно в любом месте
* `private` - поле является приватным. Читать и изменять значение этого поля можно только из методов самого класса
* `protected` - поле является защищенным. Чистать и изменять значение этого поля можно из методов самого класса и его наследников

```typescript
class A {
    public a: number;
    private b: string;
    protected c: boolean;
}

const a = new A();

a.a = 10;
a.b = "lalaka"; //Error: Property 'b' is private and only accessible within class 'A'.
a.c = true; //Error: Property 'c' is protected and only accessible within class 'A' and its subclasses.

const q1 = a.a;
const q2 = a.b; //Error: Property 'b' is private and only accessible within class 'A'.
const q3 = a.c; //Error: Property 'c' is protected and only accessible within class 'A' and its subclasses.
```

Модификаторы доступа полей нужны для того, чтобы правильно инкапсулировать логику внутри класса. `private` и `protected` поля хранят внутренне состояние класса. Это позволяет классу гарантировать, что значения в этих полях будут соответствовать внутренней логике класса (или потомка). Важно, что защита полей присутствует только на этапе компиляции. В JavaScript нет (пока) модификаторов доступа полей класса, поэтому в runtime значения приватных полей может быть изменено откуда угодно.

Если у поля не задан никакой модификатор доступа, то это поле `public` по умолчанию.

Для того, чтобы поле было доступно только на чтение, в TypeScript существует модификатор поля `readonly`. Он может применятся в месте с модификатором доступа. `readonly` поле должно быть инициализировано либо во время объявления, либо в конструкторе (об этом позже).

```typescript
class A {
    public readonly a: number = 10;
    private readonly b: string;

    constructor() {
        this.b = "lalaka";
    }
}

const a = new A();

const q1 = a.a;
a.a = q1; //Error: Cannot assign to 'a' because it is a read-only property.
```

Поле класса может быть статичным. Статичное поле - это поле, значение которого хранится не в каждом конкретном инстансе класса (у каждого свое), а в самом объекте класса. Статичное поле помечается модификатором `static`. К статичному полю можно применять модификаторы доступа и модификатор `readonly`.

```typescript
class A {
    static readonly a: { p: string } = { p: "lalaka" };
    private static readonly c: string = "lalaka";
}

const q1 = A.a; //q1 = { p: "lalaka" }
A.a.p = "malaka"; //A.a = { p: "malaka" }
q1.p; //q1 = { p: "malaka" }
```

Важно отметить, что к статичному полю через инстанс класса доступа нет.

```typescript
new A().a //Error: Property 'a' is a static member of type 'A'
```

Из-за того, что статичные поля присваиваются только объекту самого класса, а не статичные - только инстансу класса, в TypeScript можно объявить у одного и того же класс статичное и не статичное поля с одинаковым именем:

```typescript
class A {
    static a: number = 10;
    a: boolean = true;
}

A.a // 10
new A().a // true
```

Поля класса могут быть необязательными. Для этого после названия параметра нужно указать символ `?` (как у объектов).

## Конструктор

Выше уже было показано, как объявляется конструктор класса. Конструктор класса - это функция, которая вызывается для создания нового инстанса класса. Конструктор объявляется при помощи ключевого слова `constructor`. В конструкторе можно изменять значения полей класса (как публичных, так и приватных).

```typescript
class A {
    private a: number;
    public b: string;
    constructor() {
        this.a = 10;
        this.b = "lalaka";
    }
}
```

Ключевое слово `this` ссылается на тот инстанс класса, который сейчас конструируется. Этот же инстанс класса вернется в результате команды `new A()`. Конструктор класса - это функция. Она может принимать параметры. Параметры конструктора типизируются также, как типизируются параметры обычной функции.

```typescript
class A {
    private a: number;
    private b: string;
    private c?: boolean;
    constructor(a: number, b = "lalaka", c?: boolean) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}

const a1 = new A(10);
const a2 = new A(15, "malaka");
const a3 = new A(20, "balaka", false);
```

Выше также было указано, что поля класса можно инициализировать во время объявления (in-place).

```typescript
class A {
    private a: number = 10;
}
```

При создании инстанса класса, in-place поля инициализируются до вызова конструктора. К их значениям можно получить доступ из конструктора. Также в конструкторе можно изменить in-place значение поля.

```typescript
class A {
    private a: number = 10;
    public b: number = 100;

    constructor() {
        this.b = this.a
    }
}

new A().b // 10
```

Для удобства работы в TypeScript есть возможность объявлять поля сразу в конструкторе класса. Если параметру конструктора указать модификатор доступа, то у класс появится поле с именем и значением параметра и указанным модификатором доступа:

```typescript
class A {
    constructor(private a: number, public b: string) {
    }
}

const q1 = new A(10, "lalaka");
q1.b // "lalaka"
```

## Методы

Помимо полей, класс может иметь методы. Метод класс - это функция, которая существует в рамках инстанса класса (статичная в рамках объекта класса). Эта функция имеет доступ к внутреннему состоянию класса. Она, как и другие функции, может иметь или не иметь возвращаемое значение и параметры класса (объявлять generic-параметры).

```typescript
class A {
    private q: number = 2;

    multiply(a: number): number {
        return this.q*a;
    }
}

const a = new A();
const q1 = a.multiply(15); // 30
const q2 = a.multiply(10); // 20
```

Методы класса могут иметь модификаторы доступа. Они такие же, как и модификаторы доступа полей. `public` методы могут быть вызваны, как внутри класса, так и снаружи; `private` - только внутри класса; `protected` - внутри класса или внутри класса наследника.

```typescript
class A {
    public say() {
        console.log(this.getWord("Hello"));
    }

    private getWord(word: string): string {
        return word;
    }
}

const a = new A();
a.say();
a.getWord("lalaka"); //Error: Property 'getWord' is private and only accessible within class 'A'.
```

Модификатор `readonly` к методам не применим. А вот модификатор `static` применим и работает похожим образом, как и для полей. Метод присваивается не инстансам класса, а самому объекту класса. Соответственно, статичный метод **не имеет** доступа к полям инстансов класса, но имеет доступ к статичным полям и другим статичным методам.

```typescript
class A {
    private static word: string = "Hello";

    public static say() {
        console.log(this.getWord())
    }

    private static getWord(): string {
        return this.word;
    }
}

A.say();
```

## Generic-параметры

Классу можно указывать generic-параметры. Их нужно указывать в `<>` сразу после имени класса. Generic-и выводятся по тем же принципам, что и при вызовах функций.

```typescript
class A<T> {
    public readonly a: T
    constructor(a: T) {
        this.a = a;
    }
}

const a1 = new A(10);
const a2 = new A("lalaka");
const a3 = new A<true>(true);
```

Методы класса могут объявлять собственные generic-и и использовать generic-и объявленные для класса.

```typescript
class A<T> {
    public readonly a: T
    constructor(a: T) {
        this.a = a;
    }

    print<R>(b: R) {
        console.log(this.a, b);
    }
}

const a = new A(10);
a.print("lalaka");
a.print<true>(true);
```

## Наследование

В TypeScript поддерживается возможность наследования классов. Наследование - одна из ключевых механик объектно-ориентированного программирования. При помощи наследования можно передать (унаследовать) поведение одного класса другому классу.

```typescript
class Figure {
    print() {
        console.log("I'm a figure");
    }
}

class Circle extends Figure {
    radius: number = 15;
}

const q1 = new Circle();
q1.radius; // 15
q1.print();
```

В примере выше класс `Figure` содержит метод `print`. Класс `Circle` наследуется (расширяет) класс `Figure`. В этом случаи `Circle` является **наследником** класса `Figure`, а класс `Figure` - **базовом классом** (или **родительским классом**, или **суперклассом (superclass)**) для `Circle`. Все поведение, которое определяется в классе `Figure` (в нашем случаи это метод `print`) передается классу `Circle`. При этом сам класс `Circle` объявляет свое собственное специфичное поведение (поле `radius`) в дополнение к унаследованному. Поэтому инстанс класса `Circle` имеет и поле `radius` и метод `print`.

Если у базового класса объявлен конструктор с обязательными параметрами, то его наследник в своем конструкторе должен вызывать конструктор базового класса. Иначе будет ошибка компиляции.

```typescript
class Rect {
    constructor(public width: number, public height: number) {
    }
}

class Square extends Rect {
    constructor(side: number) {
        super(side, side);
    }
}

const rect = new Rect(15, 10);
const square = new Square(5);
```

Ранее были рассмотрены модификаторы доступа полей и методов. Для того, чтобы сделать какое-то поле или метод доступным только внутри класса или внутри наследников этого класса, используется модификатор `protected`. `private` поля и методы базового класса доступны только внутри самого базового класса и наследникам не передаются.

```typescript
class Rect {
    private width: number;
    private height: number;

    constructor(width: number, height: number) {
        this.width = width;
        this.height = height;
    }

    protected calcArea(): number {
        return this.width*this.height;
    }
}

class Square extends Rect {
    constructor(side: number) {
        super(side, side);
    }

    printArea() {
        const q =  this.width; //Error: Property 'width' is private and only accessible within class 'Rect'.
        console.log(this.calcArea());
    }
}

const square = new Square(5);
square.printArea();
square.calcArea(); //Error: Property 'calcArea' is protected and only accessible within class 'Rect' and its subclasses.
```

Так как наследник расширяет базовый класс - он является надмножеством над базовым классом. С точки зрения типов это означает, что инстансы наследников базового класса могут передаваться туда, где требуется значение с типом базового класса.

```typescript
class Rect {
    constructor(public width: number, public height: number) {
    }
}

class Square extends Rect {
    constructor(side: number) {
        super(side, side);
    }

    onlySquareMethod() {
    }
}

const q1: Square = new Square(10);
const q2: Rect = q1;

q1.onlySquareMethod();
q2.onlySquareMethod(); //Error: Property 'onlySquareMethod' does not exist on type 'Rect'.
```

В коде выше переменная `q2` имеет тип `Rect`, но ей присваивается значение `q1: Square`. Это возможно, потому что тип `Square` имеет все поля типа `Rect`. Однако, не смотря на то, что в `q2` лежит инстанс класса `Square`, поля и методы этого типа использовать нельзя, потому что тип переменной `q2: Rect` и TypeScript в общем случаи не может гарантировать, что в значении этой переменной лежит `Square`.

Наследоваться сразу от двух классов нельзя. TypeScript (как JavaScript) не поддерживает множественное наследование. Можно наследовать один класс от второго, а второй от третьего.

В TypeScript класс может быть абстрактным. Абстрактный класс - это класс, от которого можно наследовать другие классы, но нельзя создавать инстансы этого класса. Абстрактный класс помечается ключевым словом `abstract`.

```typescript
abstract class Figure {
    print() {
        console.log("I'm a figure");
    }
}

class Circle extends Figure {
}

const circle = new Circle();
const figure = new Figure(); //Error: Cannot create an instance of an abstract class.
```

Абстрактные классы могут иметь абстрактные методы. Абстрактные методы - это методы, у которых объявлена сигнатура, но не объявлено тело метода. Абстрактный метод помечается модификатором `abstract`. Наследник абстрактного класса обязан объявить у себя метод с такой же сигнатурой и определить реализацию этого метода. Если наследник не реализует абстрактные методы - будет ошибка компиляции.

```typescript
abstract class A {
    abstract show(p: number): string;
}


//Error: Non-abstract class 'B1' does not implement inherited abstract member 'show' from class 'A'.
class B1 extends A {
}
```

Если наследником класса с абстрактными методами тоже является абстрактный класс, то абстрактные методы реализовывать не обязательно.

```typescript
abstract class A {
    abstract show(p: number): string;
}

abstract class B2 extends A {
}
```

Абстрактные методы стоит воспринимать как интерфейс. Они гарантируют, что независимо от реализации абстрактного класса, они будут реализованы и иметь фиксированную сигнатуру.

```typescript
abstract class BasePrinter {
    abstract print(n: number): void;
}

class ConsolePrinter extends BasePrinter {
    print(n: number): void {
        console.log(n);
    }
}

class DomPrinter extends BasePrinter {
    print(n: number): void {
        const element = document.querySelector("#print");
        if (element != null) {
            element.textContent = n.toString();  
        }
    }
}

const printers: BasePrinter[] = [new ConsolePrinter(), new DomPrinter()];
printers.forEach(p => p.print(10));
```

## Класс, как интерфейс

Инстанс класса - это объект. Как и любой другой объект в TypeScript, он имеет тип. Интерфейсом взаимодействия с инстансом класса из внешнего мира являются его публичные поля и методы. Поэтому, если тип объекта требует наличие каких-либо полей или методов у объекта, и тип класса имеет все эти поля и методы, то инстансы этого класса совместимы с типом объекта.

```typescript
interface A {
    x: number;
}

class B {
    constructor(public x: number) {
    }
}

class C {
    constructor(public x: string) {
    }
}

const a1: A = new B(10);
const a2: A = new C("10"); /* Error:
Type 'C' is not assignable to type 'A'.
  Types of property 'x' are incompatible.
    Type 'string' is not assignable to type 'number'.
*/
```

Тип класса сам может выступать в роли интерфейса.

```typescript
class A {
    public x: number = 15;
}

const a: A = { x: 40 };
```

В коде выше переменная `a` типизирована, как `A`. При этом `A` - это класс. Конструкция `a: A` **не** требует присвоения только инстансов класса `A`. Это конструкция требует любое совместимое с интерфейсом класса значение.

Для более строгой типизации `TypeScript` поддерживает реализацию интерфейса классом. Для этого используется ключевое слово `implements`.

```typescript
interface Figure {
    print: () => void;
}

class Circle implements Figure {
    print() {
        console.log("I'm a circle");
    }
}
```

Если указано, что класс реализует какой-то интерфейс, но в классе (или его базовом классе) не объявлены какие-то поля или методы, указанные в интерфейсе, то TypeScript выдаст ошибку компиляцию

```typescript
interface Figure {
    print: () => void;
}

class Circle implements Figure {
} /* Error:
Class 'Circle' incorrectly implements interface 'Figure'.
  Property 'print' is missing in type 'Circle' but required in type 'Figure'.
*/
```

Класс может реализовывать сразу несколько интерфейсов - их нужно перечислять через запятую после ключевого слова `implements`. Класс может одновременно наследоваться от другого класса и реализовывать множество интерфейсов.

```typescript
interface A {
    a: number;
}

interface B {
    b: string;
}

class X {
    public a: number = 300;
}

class Y extends X implements A, B {
    constructor(public b: string) {
        super();
    }
}
```

Как было сказано ранее, классу `Y` не обязательно реализовывать интерфейсы `A` и `B`, для того, чтобы типы его инстансов были совместимы с этими интерфейсами. Реализация интерфейсов во многом нужна для более строгой типизации и облегчения работы в IDE.

## Объект класса, как тип

Ранее уже говорилось, что объект класса - это своего рода функция, которая конструирует другие объекты (инстансы класса). Так как это объект, соответственно у него есть какой-то тип.

Объект класса можно вызывать как функцию с определенными параметрами, которая вернет определенное значение. Но есть одна важная особенность. Эту функцию-конструктор можно вызывать только с ключевым словом `new`. Поэтому тип объекта класса можно описать при помощи callable со следующей модификацией:

```typescript
interface ClassType {
    new (a: number, b: string): {a: number, b: string, c: boolean}
}
```

Выше был объявлен тип, который описывает множество объектов, которые можно вызывать с оператором `new` и двумя параметрами с заданными типами, и в результате этого вызова вернется объект с заданным интерфейсом.

```typescript
interface ClassType {
    new (a: number, b: string): {a: number, b: string, c: boolean}
}

class A {
    c: boolean = false;
    constructor(public a: number, public b: string) {
    }
}

class B {
    constructor(public a: number, public b: string) {
    }
}

const classType1: ClassType = A;
const classType2: ClassType = B; /* Error:
Type 'typeof B' is not assignable to type 'ClassType'.
  Property 'c' is missing in type 'B' but required in type '{ a: number; b: string; c: boolean; }'.
*/
```

Например, можно объявить тип-помощник, который будет возвращать тип объекта класса.

```typescript
type TypeOf<T> = { new (...args: any[]): T }

class A {
}

class B {
    constructor(public b: string) {
    }
}

const classA: TypeOf<A> = A;
const classB: TypeOf<B> = B;
const wrong: TypeOf<A> = new A(); /* Error:
Type 'A' is not assignable to type 'TypeOf<A>'.
  Type 'A' provides no match for the signature 'new (...args: any[]): A'.
*/
```

## Задания

* [Задание #9](https://www.typescriptlang.org/play?#code/PQKgBAsAUGtouCCAYQQ-CCA4QQEiCsEIghWEDAYwBsBDAZ1LACEyBTaOMQDBBBBEGUD4QQbhBAZEEHYQQYRBAnCC4wgRhBOgLhBUYAESAsEDaAWEFE4eYNpjHjpYKXMVhmajeMSAeEFTaFHQPIgYZIAEQNoCYQHPclgsqND0SAxEFSo9HCA+CBszBx4ToDSIOwcgLwgOHGotuKKiGBOYKlxzJxSCpyAbCBxbKJRWFxggOIgqOGlACwADNAgwNDQoGDySiIqRqiaYK3QRGQUAIJgNAAeAC40AHYAJhTUpDRgAN5BsPgA9gukswBOAK74s3vHABTTAFxgC6cAtgBGNMcAlFs7DGCkpwADh9bgAaGTjaSfADcvwAvu0YHBAadXoQAJb4AjEQj4a4ATweTzeH0+RJe72OPyRfzAxxos1OxwWYFmAAt0aQAHTTMAAajA+NhNLACKgYpGJHIVCmc0WKyotGpDH2hxO50uN0J-xO6IWAHNvtsRQwAcCbgBGRrg-Ew+GIhgotGY7G4gnkklfZW0ukMpkswHEY7rACSC1m13ZnK5tv5guFDDFEqgqqOrvwpxImtIDzWNAA2gBdMAAXjA+YWNAA7mBxtcrZ9wZWa5RrtIrVDC8LU7N-i9S+nM8Rs1znsRAbdSwA+MDTLn4HF4ufTT6fLn0pbnGjXa7EcH4b4lmfEOMH7sHUh7Qg0LmEPb664A552qCdAC0H6GbSgHRAv3kpimIkWCIPYyTmGAAACsykG+MzAhccHHMcVx2GwQFVIkUQGFgjhVJI4KZKkWBsPYUSAKIgYAFBwxTYSI5SVOoAzuJkgAiIKgcSINwJSmGA9g4OgiQBFyvyAKgg4RSJkgCyIJ4lHMLhbD4YglFMYMCSiHwCg4Fg4KpKYaA2CR9hgAAKgAyhkUQKIg8n9CI4h8Fk6EGdY-ioOC7AKI4IgJHgphsMUpQOIp2CIBUYA8FpzBcHcvxsrMsyAjmwDAFWaVcrM+LAqQ+DHOigKzCQBpclc+rAEsez4KQwBssQyyvHsewANbAPS160G+Cx7PM1WZdluX5bMb4AMxvgAnFy8XPIQADEb4wXB0wIUNHwoccb77M8zyLDBLTfr+XT6IYqkmBBwyvsAUELfBNCIatVwjIuQ7ZvmjSFjGwopVdsE3XdyEPVAzaKus9bWjIHYvu+n6tEAA)

* [Задание #10](https://www.typescriptlang.org/play?#code/PQKgBAsAUGtouCCAYQQ-CCA4QQEiCsEIghWEDILhBBuEEUEEQEsADTFTC1WUoBUBPABwFNo4xAMEBOUB8IIUAyIIHYQQMIggThBcYQIwgQ-DQBEgLBABgFhA5OUWAGZ5+JdTCqNYcntSz8iQDwgqI+sKB5EDDJAAiADATCA53i2vSoooiAYiCoqFxwgPggAiSEeF6A0iCChIC8IDipqK74GohgXgQCqSRCNOpCgGwgqQJyiVjCYIDiIKhxNUoATAAMYACMAKxK0CDA0NCgYGqastq6+gL4YMPQAMYANgCGAM6blGAA3tAAvqNQAC5s7EwXYAC8YOsAdswA3CcAlg+n7ABOAGbry0uAEF9pFYAAPABcYAeAFcALYAIx+rygxyg0A+Xz+AMuACFQTA4KxvljoQAKZjQzanUkPADmAEpbgA+MAANwA9m8ACao9HQX6wh7LU5vTkPMDLb7sdZfclQmEI5HfAA0YHOHGhFBYHEZ0JBADIwASDkTYDLTrDvpKHuwAO4ai4Kxn8k7S2Xy-rqiiMgB0JKx5I6nSUjLG4AAtNHFiMMVA1lsdgB5U4ACx+hO4ywlNO+sNFnO+5NYsMRqzeyzAirhSJ+6tL5crYCpSrr32ZZu46IFUFAYImAlstgyWEQ7iy9jAAAFTptI+xwRxRQvvt8i24hzhGhlEuYsJ5Gop1QUclgBO5EoBREDA5UIVT3sjqDUs1hoBUAIiCoVKIETVWxgO4ODoBk4R+gOgCoIHE75gIAsiB0DeJAHgIR6IDer7zGA6RyOI6g4Fg6o5LYaAuOe7hgIwADK+SJOopBYLMVj4OIhTEagLhhKg6qCOoniyOkeC2AIVQ1B4KHYIg9RgKIeEkMIkIDmmpynKwmyQsAwD2lpfqauwmzSm8rCnBsDJ+kW9LADynLLJswBpo8PKIpynIANbADKqyyps7CRg8nJfLZun6aSRmRgAzJGACcfpKfCqwAMSRnOC5LuwK4-Ou3yRjm8LwuwnybEMcYRoOmgWPoNhTksfbADOyWLsupyrplKwynK7Dkt6YCphmHYldGkaxkAA)

* [Тест](https://docs.google.com/forms/d/e/1FAIpQLScGHVFdzcK747iP5ZToxucNWiFQSRcSrmwk6MC_nA9fQA7ZQQ/viewform?usp=sf_link)

* [Пример решения задания #9](https://www.typescriptlang.org/play?#code/PQKgBAsAUGtouCCAYQQ-CCA4QQEiCsEIghWEDAYwBsBDAZ1LACEyBTaOMQDBBBBEGUD4QQbhBAZEEHYQQYRBAnCC4wgRhBOgLhBUYAESAsEDaAWEFE4eYNpjHjpYKXMVhmajeMSAeEFTaFHQPIgYZIAEQNoCYQHPclgsqND0SAxEFSo9HCA+CBszBx4ToDSIOwcgLwgOHGotuKKiGBOYKlxzJxSCpyAbCBxbKJRWFxggOIgqOGlACwADNAgwNDEAEakAC4ATsT43QQk5FS0YADeQbD4APYAdj29AK6Ds70AFAAOyx2EAJb4YAAeAFxg88sAth00vQA0YFu9s900gzQAJmAAnudL+3mAHMAJSTaZgAC+0AhnSWAyG+GIhHwGz+F2ut16IPOlxudwA3NBoVBoKAwPIlCIVEZUJowK1oEQyBQAIJgGjHN7zT4UaikGjgmBwOaLPqrbrrDZnDH47FChgMUjLLZ3aWPaSs6QgonC2AkiE7PaHAjI1HovFYnGyrEKxWwXo0brLXrzMDdAAW+1IADpjmAANS-XUMEkGqDM0aUDlcmg8vnjKZ6ggLJYSqXogHAsFJ+2wZWqzYARkajx+Ooh4YYRoORyRKLRuMxdxzEIYjudrqexF6AoAkvNuhtPd6feXA8HK8SYRHU4izcsSJLe+d+TQANoAXTAAF4wOv5jQAO5gVkbEsgx6Hk+UDbSEvaze60U9MDKq6700oxfEZe+q7EFs0q7gAfCcPr1qixx+iCII+o6nyrDQGwbMQjz4GCO5gcQE4Yc+qazIQNA+oQsxAhs74VlA5IALR0QybSktRIAQvIpimIkWCIPYyTmGAAAC3SkDRnKqoMIm9C8vR2GwHFVIkUQGFgjhVJIjyZKkWBsPYUSAKIgYAFBwxSKSI5SVOodLuJkgAiIKgcSINwJSmGA9g4OgiQBD6EKAKgg4RSJkgCyIJ4+nMMpbCqYg+kWfSCSiHwCg4FgjypKYaA2Fp9hgAAKgAyhkUQKIgoW0iI4h8FkslpdY-ioI87AKI4IgJHgphsMUpQOOF2CIBUYA8AlzBcKcEIet03RbKQpzAMAR6zT63Q-KqpD4L0+xbN0JDAj66xAsAnyzPgpDAB6xA8h0syzAA1sAjpEbQNHzK8NBHQtS0rWt3Q0QAzDRACcPqjVchAAMQ0UJInHGJn13FJNFzFcVxxkJLSMWS4CUgYJUmHxjLUcAAng6J7zQ5J6xMguS7rKQ66NJuY66tNBPCUT4kw2TUDXmMArnqWMgPlRtH0a0QA)

* [Пример решения задания #10](https://www.typescriptlang.org/play?#code/PQKgBAsAUGtouCCAYQQ-CCA4QQEiCsEIghWEDILhBBuEEUEEQEsADTFTC1WUoBUBPABwFNo4xAMEBOUB8IIUAyIIHYQQMIggThBcYQIwgQ-DQBEgLBABgFhA5OUWAGZ5+JdTCqNYcntSz8iQDwgqI+sKB5EDDJAAiADATCA53i2vSoooiAYiCoqFxwgPggAiSEeF6A0iCChIC8IDipqK74GohgXgQCqSRCNOpCgGwgqQJyiVjCYIDiIKhxNUoATAAMYACMAKxK0CDA0NCgYGqastq6+gL4YMPQAMYANgCGAM6blGAAlgC2rKvsB+wAdgAuOwCCADRgAEJgAN6RsMsA9ueblwBOAFdlpdPn8ABSsAEAI1We2WYAAHgAuMDnAEHKHsP4ASle7zAAF9RjA4Kw-nsrmDmCjfuTzgBzXFvEncD7fTafE4AOlWn3pVIelwAFntNlyEdiANz4olQWXQS5sdhMJVgAC8r1R7AA7mDkaj0ZicSibmAAGRPQnSqDQCmXLEAM3Wy2VpuZ3H1aIxWOt8qgdsdzuVz3dpLplxRVJp-wpjPVAD4wAA3T57AAmvuJDoB52Be2+YGWf3Y63tepRXqNgqVKIoLA42JN5stodgxcuAL+5y12rAio4eql0D9RZLZf6Dwo2K5ZLtYI6nSU2LG4AAtOvFiMbVA1lsdgB5YVYvEswvs-5AkHgyEwuGIiuGrEPG+w+HUg3enEn7iyv2gfFqLYtgZFgiDuFk9hgAAAtcq7sAiHDAnBfx-KCbgCMBjQZIk5hYJ4jSKA8BQ5FgAjuIkgCiIGA5SEFUOGyHUDSWNYNAFIAIiCoKkiAiNUthgO4ODoBk4RcvigCoIHErFgIAsiB0FRJB4QIBGIFRzHzGA6RyOI6g4FgDw5LYaAuKR7hgIwADK+SJOopBYLMVj4OIhSGagLhhKgDyCOoniyOkeC2AIVQ1B4SnYIg9RgKIOkkMISL4kKlyXKwmxIsAwDahlXL9uwmxFnsrCXBsDJcqC9LAGmnzLJswBCus5xplCnyfAA1sAxYnFs7Crucnz2tV2W5eSBWrgAzKuACcXIJQcqwAMSrrB8GIZcyGoX8q5fAcZxXJsQxbiuExmBY+g2JBSxQGl0GLQh7BIVia0rMWpbsGCE5gIeQpYsuF1rhuwxAA)
