
TypeScript применяется для разработки веб-приложений с использованием популярного фреймворка Angular. В этом материале мы рассмотрим основы TypeScript, необходимые для того, чтобы приступить к освоению Angular и к работе с ним.
Предварительная подготовка
Прежде чем пользоваться TypeScript, его надо установить:
sudo npm install -g typescript
Теперь можно переходить к изучению возможностей языка. Откройте текстовый редактор и создайте новый файл. Поместите в него следующий код:
function log(message) { console log(message); } let message = 'Hello Typescript'; log(message);
Сохраните его как
main.ts и, перейдя в терминале туда, где его сохранили, выполните следующую команду:tsc main.ts
Данная команда создаёт новый файл,
main.js, который является транспилированной JS-версией файла main.ts. Этот файл можно, например, выполнить с помощью Node.js:node main.js
Обратите внимание на то, что вам не нужно выполнять команду
tsc при сборке Angular-приложения, так как инструмент ng serve подготовит код к выполнению автоматически.Типы данных в TypeScript
TypeScript поддерживает различные типы данных. Среди них можно отметить следующие:
let a: number //например: 1, 2, 3 let b: boolean //например: true, false let c: string //например: "abel agoi" let d: any //такая переменная может содержать значения любых других типов let e: number[] //числовой массив, например: [1, 3, 54] let f: any[] //массив значений любых типов, например: [1, "abel agoi", true]
Обратите внимание на то, что TypeScript поддерживает ещё один тип,
enum, о нём вы можете почитать самостоятельно.Стрелочные функции
В JavaScript функции объявляют так:
let log = function (message) { console.dir(message); }
В TypeScript того же эффекта можно добиться с помощью стрелочных функций, при объявлении которых используется последовательность символов
=>. Вот как это выглядит:let log = (message) => { //тут мы просто убираем слово function console.dir(message); } //это можно сократить, приведя к следующему виду let log = (message) => console.dir(message); //а если функции передаётся всего один параметр, можно записать её ещё короче let log = message => console.dir(message); //однако, такой код плохо читается
Интерфейсы
Не рекомендуется писать функции, которым надо передавать очень много параметров. Например, это может выглядеть так:
let myFunction = ( a: number, b: number, c: string, d: any, e: number[], f: any[] ) => { console.log('something); }
Избежать таких конструкций можно, включив параметры в объект и передав функции единственный объект. Тут нам помогут интерфейсы. Этот механизм есть в TypeScript:
interface MyParameters { a: number, b: number, c: string, d: any, e: number[], f: any[] ... ... } let myFunction = (myParameters: MyParameters) { }
Классы
Стоит выработать у себя привычку группировать связанные переменные (свойства) и функции (методы) в единую конструкцию, которая в программировании называется классом. Для того, чтобы опробовать это на практике, создайте файл
myPoints.ts и поместите в него следующий код:class MyPoint { x: number; y: string; draw() { //обратите внимание на то, что со свойством x мы работаем через this console.log("X is: " + this.x); console.log("X is: " + this.y); } getDistanceBtw(another: AnotherPoint) { //посчитать и вернуть расстояние } }
Доступ к свойствам и методам классов
Мы сгруппировали связанные переменные и методы в единый класс. Теперь надо разобраться с тем, как с ними работать. Для этого нужно создать экземпляр класса:
let myPoint = new MyPoint() //MyPoint - это имя класса myPoint.x = 2; //устанавливаем свойство x myPoint.y = "a"; //устанавливаем свойство y myPoint.draw(); //вызываем метод draw
Файл
myPoints.ts можно транспилировать и запустить то, что получилось:tsc myPoint.ts | node myPoint.js
Обратите внимание на то, что прежде чем назначать свойствам класса значения, нужно создать его экземпляр. А есть ли способ задания значений свойств в ходе создания экземпляра класса? Такой способ есть и существует он благодаря конструкторам.
Конструкторы
Конструктор — это метод, который вызывается автоматически при создании экземпляров класса. Конструктор позволяет задавать значения свойств. Вот пример работы с экземпляром класса, в котором возможности конструкторов не применяются:
let myPoint = new MyPoint() myPoint.x = 2; myPoint.y = "a";
То же самое, с использованием конструктора, можно переписать так:
let myPoint = new MyPoint(2, "a");
Однако, для того, чтобы вышеприведённый пример заработал, понадобится внести изменения в класс, задав его конструктор:
class MyPoint { x: number; y: string; constructor (x: number, y: string) { this.x = x; this.y = y; } draw() { //обратите внимание на то, что со свойством x мы работаем через this console.log("X is: " + this.x); console.log("X is: " + this.y); } getDistanceBtw(another: AnotherPoint) { //посчитать и вернуть расстояние } }
Необязательные параметры в конструкторе
Что если мы решили использовать конструктор, но хотим, чтобы явное задание параметров при создании экземпляров класса было бы необязательно? Это возможно. Для этого надо использовать знак вопроса (
?) в конструкторе. Этот знак позволяет определять параметры, которые, при создании экземпляра класса, задавать необязательно:class MyPoint { x: number; y: string; constructor (x?: number, y?: string) { //обратите внимание на "?" перед ":" this.x = x; this.y = y; } draw() { //обратите внимание на то, что со свойством x мы работаем через this console.log("X is: " + this.x); console.log("X is: " + this.y); } getDistanceBtw(another: AnotherPoint) { //посчитать и вернуть расстояние } } //Этот код нормально отработает при создании экземпляра myPointA класса MyPoint let myPointA = new MyPoint() myPoint.x = 2; myPoint.y = "a"; myPoint.draw(); //Этот код нормально отработает при создании экземпляра myPointB класса MyPoint let myPointB = new MyPoint(2, "b"); myPointB.draw(); //Этот код нормально отработает при создании экземпляра myPointС класса MyPoint let myPointC = new MyPoint(2); //обратите внимание на то, что значение Y мы тут не передаём myPointC.draw();
Модификаторы доступа
Модификатор доступа — это ключевое слово, которое используется со свойством или членом класса для управления доступом к нему извне. В TypeScript есть три модификатора доступа:
public, protected и private. По умолчанию все члены класса общедоступны — это аналогично использованию с ними модификатора доступа public, то есть, читать и модифицировать их можно извне. Использование модификатора доступа private позволяет запретить внешним механизмам работу с членами класса. Например, здесь мы использовали данный модификатор со свойствами x и y:class MyPoint { ... private x: number; private y: string; //модификаторы доступа можно устанавливать и для методов public draw() { //нарисовать что-нибудь } ... } let myPoint = new MyPoint(); myPoint.x = 3;
Попытка использовать конструкцию
myPoint.x при работе с экземпляром класса приведёт к ошибке, так как свойство объявлено с модификатором доступа private.Вспомогательные средства конструкторов
Выше мы добавляли конструктор к нашему классу следующим образом:
private x: number; public y: string; constructor (x: number, y: string) { this.x = x; this.y = y; }
TypeScript позволяет записать то же самое в сокращённой форме:
constructor (private x: number, public y: string) {}
Всё остальное будет сделано автоматически (готов поспорить, вы часто будете с этим встречаться в Angular-приложениях). То есть, нам не нужен следующий код:
private x: number; public y: string; и this.x = x; this.y = y;
Геттеры и сеттеры
Предположим, что сейчас класс
MyPoint выглядит так:class MyPoint { constructor (private x?: number, private y?: string) {} draw() { //нарисовать что-нибудь } drawAnotherThing() { //нарисовать ещё что-нибудь } }
Мы совершенно точно знаем, что не сможем работать со свойствами
x и y за пределами экземпляра класса MyPoint, так как они объявлены с использованием модификатора доступа private. Если же нужно как-то на них влиять или читать их значения извне, нам понадобится использовать геттеры (для чтения свойств) и сеттеры (для их модификации). Вот как это выглядит:class MyPoint { constructor (private x?: number, private y?: string) {} getX() { //возвращает X return this.x; } setX(value) { //записывает в X переданное методу значение this.x = value; } } //так как установить x напрямую нельзя, после инициализации экземпляра класса myPoint //мы использует сеттер setX() для установки значения X let myPoint = new MyPoint(); myPoint.setX = 4; console.log( myPoint.getX() ); //будет выведено 4;
Механизм сеттеров и геттеров позволяет задавать ограничения, применяемые при установке или чтении свойств класса.
Вспомогательные механизмы для работы с сеттерами и геттерами
Вместо того, чтобы использовать конструкцию вида
myPoint.setX() для того, чтобы установить значение x, что если можно было бы поступить примерно так:myPoint.X = 4; //работа с X так, как будто это свойство, хотя на самом деле это сеттер
Для того, чтобы подобный механизм заработал, при объявлении геттеров и сеттеров нужно поставить перед именем функций ключевые слова
get и set, то есть, сравнивая это с предыдущими примерами, отделить эти слова пробелом от следующего за ними имени функции:class MyPoint { constructor (private x?: number, private y?: string) {} get X() { //обратите внимание на пробел перед X return this.x; } set X(value) { //обратите внимание на пробел перед Y this.x = value; } }
Кроме того, общепринятой практикой является использование знаков подчёркивания в начале имён свойств (
_):class MyPoint { constructor (private _x?: number, private _y?: string) {} get x() { //обратите внимание на пробел перед X return this._x; } set x(value) { //обратите внимание на пробел перед Y this._x = value; } }
Модули
Когда вы приступите к созданию реальных приложений, вы обнаружите, что в них понадобится далеко не один класс. Поэтому хорошо бы найти средство, позволяющее создавать классы так, чтобы их можно было использовать в других файлах, и в классах, объявленных в этих файлах, то есть, нам нужны инструменты написания модульного кода. Такие инструменты уже имеются в TypeScript. Для того, чтобы их рассмотреть, приведём код в файле
myPoint.ts к такому виду:export class MyPoint { //обратите внимание на новое ключевое слово export constructor (private _x?: number, private _y?: string) {} get x() { return this._x; } set x(value) { //обратите внимание на пробел перед x this._x = value; } draw() { //нарисуем что-нибудь } }
Благодаря ключевому слову
export класс, описанный в файле MyPoint.ts, можно сделать видимым в других файлах, импортировав его в них с использованием ключевого слова import.Для того, чтобы, например, пользоваться классом
MyPoint в файле main.ts, его надо импортировать:import { MyPoint } from './myPoint'; class Main { let MyPoint = new MyPoint(4, "go to go"); MyPoint.draw(); }
Обратите внимание на то, что файлы
main.ts и myPoint.ts находятся в одной и той же директории.
Итоги
В этом материале мы рассмотрели основы TypeScript, которые необходимы для того, чтобы начать писать приложения на Angular. Теперь вы сможете понять устройство Angular-кода, а значит, у вас, кроме прочего, появится возможность эффективно осваивать руководства, которые посвящены этому фреймворку и предполагают знание TS.
Уважаемые читатели! Если вы хорошо разбираетесь в Angular — просим рассказать новичкам о том, как вы его изучали, и посоветовать хорошие учебные материалы по нему.

