- PVSM.RU - https://www.pvsm.ru -
TypeScript — это надмножество JavaScript, то есть, любой код на JS является правильным с точки зрения TypeScript. Однако, TypeScript обладает некоторыми дополнительными возможностями, которые не входят в JavaScript. Среди них — строгая типизация (то есть, указание типа переменной при её объявлении, что позволяет сделать поведение кода более предсказуемым и упростить отладку), механизмы объектно-ориентированного программирования и многое другое. Браузеры не поддерживают TypeScript напрямую, поэтому код на TS надо транспилировать в JavaScript.
TypeScript применяется для разработки веб-приложений с использованием популярного фреймворка Angular. В этом материале мы рассмотрим основы TypeScript, необходимые для того, чтобы приступить к освоению Angular и к работе с ним.
Прежде чем пользоваться TypeScript, его надо установить:
sudo npm install -g typescript
Теперь можно переходить к изучению возможностей языка. Откройте текстовый редактор и создайте новый файл. Поместите в него следующий код:
function log(message) {
console(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 поддерживает различные типы данных. Среди них можно отметить следующие:
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 — просим рассказать новичкам о том, как вы его изучали, и посоветовать хорошие учебные материалы по нему.
Автор: ru_vds
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/270511
Ссылки в тексте:
[1] Image: https://habrahabr.ru/company/ruvds/blog/344502/
[2] Источник: https://habrahabr.ru/post/344502/?utm_campaign=344502
Нажмите здесь для печати.