Skip to content

Въведение в TypeScript

Какво е TypeScript?

TypeScript е строго типизиран език за програмиране, базиран на JavaScript. Той е създаден от Anders Hejlsberg през 2012 г. и в момента се разработва и поддържа от Microsoft като проект с отворен код.

TypeScript се компилира в JavaScript и може да се изпълнява във всяка JavaScript среда (например браузър или Node.js на сървър).

Поддържа множество програмни парадигми, като функционално, генерично, императивно и обектно-ориентирано програмиране, и е компилиран (транспилиран) език, който се преобразува в JavaScript преди изпълнение.

Защо TypeScript?

TypeScript е строго типизиран език, който помага за предотвратяване на често срещани грешки при програмирането и избягване на определени видове грешки при изпълнението на програмата, преди тя да бъде изпълнена.

Строго типизираният език позволява на разработчика да задава различни ограничения и поведения на програмата чрез дефинициите на типовете данни. Това улеснява проверката на коректността на софтуера и предотвратяването на дефекти. Това е особено ценно при мащабни приложения.

Някои от предимствата на TypeScript:

  • Статична типизация, по избор строго типизирана
  • Извеждане на типове (Type Inference)
  • Достъп до функционалности от ES6 и ES7
  • Съвместимост между платформи и браузъри
  • Поддръжка на инструменти с IntelliSense

TypeScript и JavaScript

TypeScript се пише в файлове с разширение .ts или .tsx, докато JavaScript файловете се пишат с разширение .js или .jsx.

Файловете с разширение .tsx или .jsx могат да съдържат разширение на синтаксиса на JavaScript JSX, което се използва в React за разработка на потребителски интерфейси.

TypeScript е типизирано разширение на JavaScript (ECMAScript 2015) по отношение на синтаксиса. Всеки JavaScript код е валиден TypeScript код, но обратното не винаги е вярно.

Например, разгледайте функция в JavaScript файл с разширение .js, като следната:

const sum = (a, b) => a + b;

Функцията може да бъде преобразувана и използвана в TypeScript, като се промени разширението на файла на .ts. Ако обаче същата функция е анотирана с TypeScript типове, тя не може да бъде изпълнена в никоя JavaScript среда без компилация. Следният TypeScript код ще доведе до синтаксисна грешка, ако не бъде компилиран:

const sum = (a: number, b: number): number => a + b;

TypeScript е проектиран да открива възможни изключения, които могат да възникнат по време на изпълнение по време на компилация, като разработчикът дефинира намеренията си с типови анотации. Освен това TypeScript може да открива проблеми и ако не е предоставена типова анотация. Например, следният фрагмент от код не специфицира никакви типове TypeScript:

const items = [{ x: 1 }, { x: 2 }];
const result = items.filter(item => item.y);

В този случай TypeScript открива грешка и я докладва:

Property 'y' does not exist on type '{ x: number; }'.

Типовата система на TypeScript е силно повлияна от поведението на JavaScript по време на изпълнение. Например, операторът за събиране (+), който в JavaScript може да изпълнява както съединяване на низове, така и събиране на числа, е моделиран по същия начин в TypeScript:

const result = '1' + 1; // Резултатът е от тип string

Екипът, стоящ зад TypeScript, е взел съзнателно решение да маркира необичайното използване на JavaScript като грешки. Например, разгледайте следния валиден JavaScript код:

const result = 1 + true; // В JavaScript резултатът е равен на 2

Въпреки това, TypeScript хвърля грешка:

Operator '+' cannot be applied to types 'number' and 'boolean'.

Тази грешка възниква, защото TypeScript строго налага съвместимост на типовете и в този случай идентифицира невалидна операция между число и булева стойност.

Генериране на код с TypeScript

Компилаторът на TypeScript има две основни отговорности: проверка за грешки в типовете и компилиране към JavaScript. Тези два процеса са независими един от друг. Типовете не влияят върху изпълнението на кода в JavaScript среда за изпълнение, тъй като са напълно премахнати по време на компилацията. TypeScript все пак може да генерира JavaScript дори при наличие на грешки в типовете. Ето пример за TypeScript код с грешка в типа:

const add = (a: number, b: number): number => a + b;
const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'.

Въпреки това, TypeScript все още може да генерира изпълним JavaScript код:

'use strict';
const add = (a, b) => a + b;
const result = add('x', 'y'); // xy

Не е възможно да се проверяват типовете на TypeScript по време на изпълнение. Например:

interface Animal {
name: string;
}
interface Dog extends Animal {
bark: () => void;
}
interface Cat extends Animal {
meow: () => void;
}
const makeNoise = (animal: Animal) => {
if (animal instanceof Dog) {
// 'Dog' е само тип, но тук се използва като стойност.
// ...
}
};

Тъй като типовете се изтриват след компилация, няма начин да се изпълни този код в JavaScript. За да разпознаем типовете по време на изпълнение, трябва да използваме друг механизъм. TypeScript предоставя няколко опции, като една от често използваните е “tagged union”. Например:

interface Dog {
kind: 'dog'; // Tagged union
bark: () => void;
}
interface Cat {
kind: 'cat'; // Tagged union
meow: () => void;
}
type Animal = Dog | Cat;
const makeNoise = (animal: Animal) => {
if (animal.kind === 'dog') {
animal.bark();
} else {
animal.meow();
}
};
const dog: Dog = {
kind: 'dog',
bark: () => console.log('bark'),
};
makeNoise(dog);

Свойството “kind” е стойност, която може да се използва по време на изпълнение, за да се разграничат обектите в JavaScript.

Възможно е също така стойността по време на изпълнение да има тип, различен от този, деклариран в декларацията за типа. Например, ако раработчикът е интерпретирал погрешно типа на API и го е анотирал неправилно.

TypeScript е надмножество (superset) на JavaScript, така че ключовата дума “class” може да се използва както като тип, така и като стойност по време на изпълнение.

class Animal {
constructor(public name: string) {}
}
class Dog extends Animal {
constructor(
public name: string,
public bark: () => void
) {
super(name);
}
}
class Cat extends Animal {
constructor(
public name: string,
public meow: () => void
) {
super(name);
}
}
type Mammal = Dog | Cat;
const makeNoise = (mammal: Mammal) => {
if (mammal instanceof Dog) {
mammal.bark();
} else {
mammal.meow();
}
};
const dog = new Dog('Fido', () => console.log('bark'));
makeNoise(dog);

В JavaScript “class” има свойство “prototype”, а операторът “instanceof” може да се използва, за да се провери дали свойството prototype на даден конструктор се среща някъде в прототипната верига на обект.

TypeScript няма влияние върху производителността по време на изпълнение, тъй като всички типове се премахват. Въпреки това TypeScript добавя известно натоварване по време на компилация (build time).

Модерен JavaScript днес (Downleveling)

TypeScript може да компилира код към всяка публикувана версия на JavaScript, започвайки от ECMAScript 3 (1999). Това означава, че TypeScript може да транспилира код, използващ най-новите JavaScript функционалности, към по-стари версии - процес, известен като Downleveling. Това позволява използването на модерен JavaScript, като същевременно се поддържа максимална съвместимост с по-стари среди за изпълнение.

Важно е да се отбележи, че по време на транспилацията към по-стара версия на JavaScript, TypeScript може да генерира код, който да доведе до забавяне на производителността в сравнение с нативните имплементации.

Ето някои от модерните JavaScript функционалности, които могат да се използват в TypeScript:

  • ECMAScript модули вместо AMD-стил “define” callback-и или CommonJS “require” изрази.
  • Класове вместо прототипи.
  • Деклариране на променливи с “let” или “const” вместо “var”.
  • “for-of” цикъл или “.forEach” вместо традиционния “for” цикъл.
  • Arrow функции вместо функционални изрази.
  • Деструктуриране присвояване.
  • Съкратени имена на свойства/методи и изчислени имена на свойства.
  • Параметри по подразбиране на функции.

Използвайки тези модерни функции на JavaScript, разработчиците могат да пишат по-изразителен и кратък код в TypeScript.