Основы синтаксиса TypeScript

66e9c80d6e0d4fce9d9e2e541770567b.png

В 2012 году разработчики C# из компании Microsoft создали язык TypeScript — надмножество JavaScript. Он предназначен для разработки больших приложений, от100 тысяч строк. Давайте на примерах рассмотрим синтаксис TypeScript, его основные достоинства и недостатки, а также разберём способ взаимодействия с популярными библиотеками.

Кому это будет полезно: Web-разработчикам и разработчикам клиентских приложений, интересующимся возможностью практического применения языка TypeScript.
Существуют различные инструменты, которые позволяют писать компилируемый в JavaScript код: CoffeeScript, Dart, Uberscript и другие. К ним относится и язык программирования TypeScript, позволяющий исправить некоторые недостатки, присущие JavaScript.

Недостатки JavaScript


  • Отсутствие модульности — могут возникать проблемы из-за конфликта больших файлов.
  • Нелогичное поведение.
  • Динамическая типизация — нет IntelliSense, из-за чего мы не видим, какие ошибки будут возникать во время написания, а видим их только во время исполнения программы.


Основные достоинства TypeScript


  • Статическая типизация.
  • Компилируется в хороший JavaScript.
  • Наличие инструментов для разработчика.
  • Нативный код хорошо читается, в нём легко разобраться.
  • TypeScript поддерживается очень многими инструментами для разработки: Visual Studio, PHP Storm и другие IDE.
  • Поддерживается ECMAScript 6.0.
  • Компилятор найдёт и выдаст ошибку несоответствия типов до начала компилирования.


Типы переменных, которые поддерживает TypeScript


  1. Number
  2. String
  3. Boolean
  4. Array
  5. Enum
  6. Any
  7. Void


Они используются для объявления переменных, функций, классов, Generic-типов и других конструкций.

Функции


Параметры функций подразделяются на необязательные и по умолчанию.

  • Необязательный параметр
    function имя_функции (имя_переменной?:тип): тип_возвращаемого_значения
  • Параметр по-умолчанию
    function имя_функции (имя_переменной?:тип = "значение"):тип_возвращаемого_значения
  • Однотипные параметры
    function имя_функции (...имя_переменной?:тип ):тип_возвращаемого_значения


По аналогии с C# знак вопроса после переменной означает, что её значение можно не передавать.

Объявление функции

[csharp]
function getCarName(manufacturerName: string, model?: string): string {
   if (model) {
       return manufacturerName + " " + model;
   }
   return manufacturerName;
}
[/csharp]


Функции обратного вызова


Мы также можем передавать в качестве параметра функцию.

[csharp]
function numberOperation(x: number, y: number, callback: (a: number, b: number) => number) {
   return callback(x, y);
}

function addCallBackNumbers(x: number, y: number): number {
   return x + y;
}

function multiplyNumbers(x: number, y: number): number {
   return x * y;
}

console.log(numberOperation(5, 5, addCallBackNumbers)); // 10
console.log(numberOperation(5, 5, multiplyNumbers)); // 25
[/csharp]


Вызывая функцию numberOperation, мы передаём два параметра и функцию в качестве callback«а.

Объединение и перегрузка функций


Несмотря на строгую типизацию, в TS есть возможность использовать одну и ту же функцию с разными типами передаваемых значений. Например, в зависимости от типа переданного значения, одна функция конкатенирует наши числа, а вторая складывает.

[csharp]
//передаём 2 параметра стринг и получаем строку
function addOvverload(x: string, y: string): string;
//передаём 2 параметра int и получаем int результ
function addOvverload(x: number, y: number): number;

function addOvverload(x, y): any {
   return x + y;
}
[/csharp]


ООП. Классы


Class имя_класса {
свойства;
методы();
constructor(свойства); }

[csharp]
class Car {
var mazda = new Car(1, "Mazda", "6");
colsole.log(mazda.getCarInfo());

class Car {
// объявляются три поля
   id: number;
   name: string;
   model: string;

// инициализируется конструктор, создающий модель
   constructor(carId: number, carModel: string, model: string) {
       this.id = carId;
       this.name = carModel;
       this.model = model;
   }

// будет отображать информацию о автомобиле
   getCarInfo(): string {
       return "Car model = " + this.name + " model= " + this.model;
   }
}

var mazda = new Car(1, "Mazda", "6");
console.log(mazda.getCarInfo());
[/csharp]


ООП. Статические свойства и функции


Для определения статических функций и свойств используется ключевое слово static.

[csharp]
class Formula {
   static PI: number = 3.14;
   static Half = 0.5;
//  расчитывается площадь круга
   static getСircleSquare(radius: number): number {
       return this.PI * radius * radius;
   }
// расчитывается площадь треугольника
   static getTriangleSquare(length: number, height: number): number {
       return this.Half * length * height;
   }
}

// созд. объект класса Formula и расчитываются значения
var circle = Formula.getСircleSquare(16);
var triangle = Formula.getTriangleSquare(4, 7);
console.log("Площадь круга = " + circle);
console.log("Площадь треугольника = " + triangle);
[/csharp]


ООП. Наследование


Одним из ключевых элементов ООП является наследование, которое в TS реализуется c помощью ключевого слова extended. При помощи extended мы можем наследовать от базового класса и описать классы наследники.

[csharp]
interface IAnimal {
// свойства интерфейса — два поля и один метод
   name: string;
   danger: number;
   getInfo(): void;
}

class Animal implements IAnimal {
// наследуемся от реализованного интерфейса IAnimal
   name: string;
   danger: number;

   constructor(name: string, danger: number) {
       this.name = name;
       this.danger = danger;
   }

   getInfo(): void {
       console.log("Класс Животное. Имя: " + this.name + ", опасность: " + this.danger);
   }
}

class Fox extends Animal {
   tailLength: number;

   constructor(name: string, danger: number, tailLength: number) {
       super(name, danger);
       this.tailLength = tailLength;
// ключевое слово super — вызывает конструктор базового класса,
// инициализирует объект с начальными описанными в нём свойствами,
// а потом инициализируются свойства класса наследника.
   }

   getInfo(): void {
       super.getInfo();
       console.log("Класс Лиса. Длина хвоста: " + this.tailLength + " м");
   }
}

var goose: Animal = new Animal("Гусь", 1);
goose.getInfo();

var fox: Animal = new Fox("Фоксик", 10, 1);
fox.getInfo();
[/csharp]


ООП. Интерфейсы


Для определения кастомного типа данных без реализации в TS (и не только) используются интерфейсы. Чтобы объявить интерфейс, используется ключевое слово Interface.

[csharp]
module InterfaceModule {
// объявляется интерфейс IAnimal и описываются основные поля и методы этого класса
// и потом они реализуются непосредственно на классах наследниках.
   interface IAnimal {
       name: string;
       danger: number;

       getInfo(): string;
   }

   class Animal implements IAnimal {
       name: string;
       danger: number;

       constructor(name: string, danger: number) {
           this.name = name;
           this.danger = danger;
       }

       getInfo(): string {
           return this.name + " " + this.danger;
       }
   }

   var seal: IAnimal = new Animal("Тюлень", 1);
   console.log(seal.getInfo());
}
[/csharp]


ООП. Инкапсуляция


Для сокрытия внешнего доступа к состоянию объекта и управления доступом к этому состоянию, в TS используется два модификатора: public и private.

Внутри нашего класса мы можем писать недоступные извне методы и манипулировать с их помощью.

Реализуется это как в C#:

[csharp]
module Encapsulation {
   class Animal {
       private _id: string;
       name: string;
       danger: number;

       constructor(name: string, danger: number) {
// заполнение приватного поля _id при создании метода в конструкторе.
           this._id = this.generateGuid();
           this.name = name;
           this.danger = danger;
       }

       private generateGuid(): string {
           var d = new Date().getTime();
           var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
               var r = (d + Math.random() * 16) % 16 | 0;
               d = Math.floor(d / 16);
               return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
           });
           return uuid;
       }

       public getInfo(): string {
           return "Id = " + this._id + " name = " + this.name + " danger = " + this.danger;
       }
   }

   var parrot: Animal = new Animal("Кеша", 1);
   console.log(parrot.getInfo());
[/csharp]


Таким образом мы ограничили доступ к методу generateGuid(). По умолчанию поля и методы имеют доступ public.

ООП. Generic


TypeScript позволяет создавать Generic-типы.

function имя_функции(имя_переменной: Т): Т
Где Т — тип, которым типизирована функция. Также TS поддерживает типизацию интерфейсов и классов.

Class имя_класса
Interface имя_интерфейса
Где T — тип, которым типизирована функция.

[csharp]
module GenericModule {
  function getId(id: T) : T {
       return id;
   }
// Generic класс Animal, при создании мы передаём тип generic типа и устанавливаем id 
   class Animal {
       private _id: T;

       constructor(id: T) {
           this._id = id;
       }

       getId(): T {
           return this._id;
       }
   }

   var cat = new Animal(16);
   console.log("Cat id = " +  cat.getId());

   var dog = new Animal("2327c575-2f7c-46c3-99f2-a267fac1db5d");
   console.log("Dog id = " + dog.getId());
}
[/csharp]


Передаём тип, устанавливаем Id для определения типа и возвращаем нашему животному информацию. Таким образом используются Generic-типы.

Модули


Одним из недостатков JavaScript является то, что большое количество файлов может пересекаться между собой, и возникают своеобразные конфликты. В TypeScript эту проблему решают модули.

Модули — это пространство имен, внутри которого можно определить классы, перечисления, переменные, функции, другие модули. Любой набор классов, интерфейсов, функций могут быть объединены в какую-то скобку и названы определённым модулем. И потом с этим модулем можно легко и просто взаимодействовать.

Для определения модуля используется ключевое слово module.

[csharp]
import test = MyTestModule.MyTestClass;
module CarModule {
// объявляется модуль CarModule в нём есть интерфейс iCar.
   export interface ICar {
// ключевое слово export говорит нам о том, что данный интерфейс может быть видет извне нашего модуля. Если мы уберем слово export, данный интерфейс будет виден в скобках этого CarModule. 
       id: number;
       carModel: string;
       model: string;

       getCarInfo(): string;
   }

   export class Car implements ICar {
// создается класс Car, который имплементирует наш ICar
       id: number;
       carModel: string;
       model: string; // если мы удалим это поле, то IntelliSence предупредит о том, что не описано поле model

       constructor(carId: number, carModel: string, model: string) {
           this.id = carId;
           this.carModel = carModel;
           this.model = model;
       }

       getCarInfo(): string {
           var t = new test().getInfo();
           return "Car model = " + this.carModel + " model= " + this.model + " " + t;
       }
   }
}

let car = new CarModule.Car(16, "ВАЗ", "2107");
console.log(car.getCarInfo());
[/csharp]


Изменения в терминологии: важно, что начиная с TypeScript 1.5 изменилась номенклатура. Чтобы не было разногласий с терминологией ECMAScript 2015, «внутренние модули» стали называться «namespaces», а «внешние модули» теперь просто «modules». То есть module X { однозначно с более предпочитаемым namespace X {.

Заголовочные файлы


Для связки с какими-то глобальными переменными необходимо подключать заголовочные файлы — это один из недостатков TypeScript. Чтобы установить связь с внешними файлами скриптов JavaScript в TS, необходимо использовать декларативные или заголовочные файлы с расширением *.d.ts.

Заголовочные файлы основных библиотек уже описаны, и с ними достаточно просто и легко работать. Для этого необходимо зайти на сайт и подключить нужный набор заголовочных файлов, например, jQuery. Затем объявляются основные глобальные переменные, которые используются в jQuery, и в последующем они будут использоваться в TS-файле.

[csharp]
/// 
class Cars {
   private cars: Array = new Array();

   load(): void {

       $.getJSON('http://localhost:53923/api/Car',
           (data) => {
               this.cars = data;
               alert('данные загружены');
           });
   }

   displayUsers(): void {
       var table = '';
       for (var i = 0; i < this.cars.length; i++) {

           var tableRow = '' +
               '' +
               '' +
               '' +
               '';
           table += tableRow;
       }
       table += '
' + this.cars[i].id + '' + this.cars[i].name + '' + this.cars[i].model + '
'; $('#content').html(table); } } window.onload = () => { var cars: Cars = new Cars(); $("#loadBtn").click(() => { cars.load(); }); $("#displayBtn").click(() => { cars.displayUsers(); }); }; [/csharp]


Недостатки TypeScript


  • DefinitelyTyped — в некоторых случаях отсутствуют популярные библиотеки.
  • .ts .d.ts .map — большое количество дополнительных файлов после компилирования ts-файла.
  • Неявная статическая типизация. Если объявим переменную типа any, то никакой пользы от статической типизации не получим.

Преимущества TypeScript


  • Строгая типизация. Огромный плюс — IntelliSence, который на этапе компиляции указывает на ошибки, и их можно исправить до этапа выполнения.
  • ООП. Прекрасно поддерживаются все основные принципы ООП.
  • Надмножество JavaScript«а. Достаточно скопировать код из JavaScript и вставить в TS, чтобы он заработал
  • Разработка сложных решений. Благодаря поддержке модульности крупные команды разработчиков могут создавать большие приложения.


Инструменты


Заключение


TypeScript является отличным враппером, который может в значительной мере повысить производительность команды разработчиков, с сохранением совместимости с существующим кодом. TypeScript поддерживается в VisualStudio 2015 и взаимодействует с ECMAScript 6.0. Пока в JavaScript нет строгой типизации, TypeScript — это отличная альтернатива, применение которой не требует значительных затрат времени на изучение.

© Habrahabr.ru