TypeScript е популярен език за програмиране, който предлага статична проверка на типа за JavaScript код. Той е разработен от Microsoft и пуснат през 2012 г. Оттогава той придоби значителна популярност сред разработчиците, особено тези, които работят по мащабни проекти.

История на машинописа

През 2011 г. разработчикът на Microsoft Anders Hejlsberg започна да работи върху TypeScript с цел да осигури по-добро изживяване при разработка на широкомащабни JavaScript приложения. Hejlsberg е известен също с разработването на C#, Delphi и Turbo Pascal, наред с други. TypeScript е проектиран да бъде надмножество на JavaScript, което означава, че всеки валиден JavaScript код също е валиден TypeScript код.

Защо да използвате TypeScript?

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

Основни характеристики на TypeScript

Ето някои от основните характеристики на TypeScript, които го правят популярен сред разработчиците:

  1. Статично въвеждане: TypeScript поддържа статично въвеждане, което означава, че променливите, функционалните параметри и върнатите типове могат да бъдат изрично въведени. Това може да улови грешки по време на компилиране и да подобри качеството на кода.
  2. Обектно-ориентирано програмиране: TypeScript поддържа концепции за обектно-ориентирано програмиране (ООП) като класове, интерфейси и наследяване. Това може да направи кода по-модулен и по-лесен за поддръжка. Това също го прави по-податлив за разработчици, които вече са запознати с OO езици като C++, Java и др.
  3. Извеждане на типа: TypeScript може да извежда типове въз основа на контекста, което означава, че разработчиците не винаги трябва изрично да въвеждат променливи. Това може да намали шаблонния код и да подобри четливостта.
  4. Декоратори: TypeScript поддържа декоратори, които са начин за модифициране на класове и свойства по време на изпълнение. Декораторите могат да се използват за регистриране, валидиране и други междусекторни проблеми.

Преди да преминем към примери за всяка от горните функции, нека първо настроимнашата среда за разработка Typescript

Настройте среда за разработка Typescript

Забележка: Следните стъпки са за настройка на чиста среда на Typescript за демонстриране на компилиране на .ts файлове. Обикновено, когато използвате рамки като React, NextJS, NestJs, Express, NodeJS,вие създавате проекти, като използвате техните начални програми и те автоматично ще настроят среда на typescript или по подразбиране, или чрез указване на флаг. Например вижте Стартер на React за създаване на проект с машинопис.

1. Инсталирайте Node.js и npm (Node Package Manager) на вашия компютър. Можете да изтеглите най-новата версия на Node.js от официалния уебсайт: https://nodejs.org/en/download/

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

npm install -g typescript

3. Създайте нова папка за вашия TypeScript проект

4. Отворете Visual Studio Code и навигирайте до папката на вашия проект с помощта на менюто „Файл“. Отидете тук, ако все още нямате Visual Studio Code

5. Създайте нов файл с разширение .ts, което е файловото разширение за TypeScript файлове.

6. Добавете малко TypeScript код към вашия файл. (Вижте по-долу за някои примери)

7. Отворете терминала във Visual Studio Code, като използвате менюто Изгледи изберете Нов терминал. Или като използвате менюто Терминал и изберете Нов терминал

8. Компилирайте вашия TypeScript код в JavaScript код, като използвате следната команда:

tsc filename.ts

Заменете filename с името на вашия TypeScript файл.

9. Компилаторът на TypeScript ще генерира JavaScript файл със същото име като вашия TypeScript файл, но с разширение .js. Вече можете да стартирате този JavaScript файл с помощта на Node.js.

node filename.js

Сега, когато вашата среда за разработка е готова и работи, стартирайте някои от примерите за себе си и вижте дали можете да въведете критична промяна, която може да се компилира успешно (без да използвате any)

Примери за ключови характеристики на Typescript

Статично въвеждане

let age: number = 30;
let name: string = "John Doe";
let isMale: boolean = true;
let ageWithError: number = "thirty"; // Type error: Type 'string' is not assignable to type 'number'

Горният код няма да бъде компилиран поради променливатаageWithError. Ако използвате IDE като Visual Studio Code, тази синтактична грешка ще бъде подчертана веднага, без да се налага да компилирате кода си.

Обектно-ориентирано програмиране

Ако идвате от традиционни ООП езици като JAVA или C++, тогава typescript ще ви се стори познат, тъй като въвежда подобни конструкции на JavaScript. Вижте следния пример:

interface Shape {
  getArea(): number;
}

class Square implements Shape {
  constructor(private sideLength: number) {}

  getArea() {
    return this.sideLength ** 2;
  }
}

class Triangle implements Shape {
  constructor(private base: number, private height: number) {}

  // OOP error: Triangle is not implementing the getArea method from the Shape interface
}

const square = new Square(5);
const triangle = new Triangle(10, 20); // Error: Class 'Triangle' incorrectly implements interface 'Shape'.

Горният код ще върне грешки при компилиране, защото Triangle не е внедрил правилно интерфейса Shape.

Typescript също поддържа типични ООП характеристики като полиморфизъм и се държи подобно на типичните ООП езици

abstract class Animal {
    public abstract makeSound()
}
  
class Dog extends Animal {
    public makeSound() {
        console.log("Bark");
    }
}

class Cat extends Animal {
    public makeSound() {
        console.log("Meaow");
    }  
}
  
const animals: Animal[] = new Array<Animal>(5)
for(let i=0; i<animals.length; ++i){
    //Some random logic to determin which type of animal to create
    if (i%2 === 0){
        animals[i] = new Dog()
    } else {
        animals[i] = new Cat()
    }
}

animals.forEach(animal => {
    animal.makeSound()
})

Горният код ще се отпечата:

Bark
Meaow
Bark
Meaow
Bark

Както можете да видите, typescript предоставя ООП характеристики като класове, абстрактни класове, интерфейси, наследяване, полиморфизъм и други типични ООП функции. И много често срещани грешки се улавят по време на компилиране.

Извод за тип

Typescript всъщност не изисква да дефинирате изрично типовете на всяка променлива. При определени обстоятелства може да направи извод за времената на заданието.

let age = "thirty";
age = 30; // Type error: Type 'number' is not assignable to type 'string'

Както можете да видите, нямаше нужда изрично да се age като string, но беше автоматично дефинирано поради присвояването на "thirty". Може да забележите, че грешката е подобна на примера по-горе за статично въвеждане. Има малък нюанс междуИзвод за типи Статично въвеждане

let ageWithStaticTyping: number = 30;
let ageWithTypeInference = 30;

ageWithStaticTyping = "thirty" //Type error: Type 'number' is not assignable to type 'string'
ageWithTypeInference = "thirty" //Type error: Type 'number' is not assignable to type 'string' 

Както можете да видите от по-горе, тънкостта между Извод за типи Статично въвежданее при изрично указване на типа при декларация като в променливата ageWithStaticType. И двата начина ще имат еднакви проверки на типа по време на компилиране, но ако трябва да избера кой подход да използвам в моя код, винаги ще избера статично въвеждане.

Декоратори

Ето често срещано използване на декоратори в машинопис

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    console.log(`Executing ${key} with arguments:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Execution of ${key} completed with result:`, result);
    return result;
  };

  return descriptor;
}

class Calculator {
  @log
  add(x: number, y: number) {
    return x + y;
  }
}

const calculator = new Calculator();
const result = calculator.add(10, 20);
console.log("Result:", result);

В горния код ние дефинираме функция декоратор, наречена log, която приема три параметъра: целевия клас, името на метода и дескриптор на свойство. Декораторът променя поведението на метода add, като добавя оператори за регистриране преди и след неговото изпълнение.

След това дефинираме Calculator клас с add метод, който приема два параметъра, x и y. Прилагаме декоратора @log към метода add, за да променим поведението му.

Накрая създаваме екземпляр на класа Calculator и извикваме метода add с аргументите 10 и 20. Декораторът променя поведението на метода add, за да регистрира неговите аргументи преди изпълнение и върнатата му стойност след изпълнение.

Ето резултата от кода:

Executing add with arguments: [10, 20]
Execution of add completed with result: 30
Result: 30

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

Друг често срещан пример за декораторите е налагането на дефиниране на определени атрибути.

function required(target: any, key: string) {
  let value = target[key];

  const getter = function() {
    return value;
  };

  const setter = function(newVal: any) {
    if (!newVal) {
      throw new Error(`"${key}" is a required property.`);
    } else {
      value = newVal;
    }
  };

  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class User {
  @required
  name: string;

  @required
  email: string;
}

const user = new User();
user.name = "Alice";
user.email = ""; // TypeScript error: Error - ""email" is a required property."

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

След това дефинираме клас User с две свойства: name и email. Прилагаме декоратора @required и към двете свойства, за да гарантираме, че са необходими.

Накрая създаваме нов екземпляр на класа User и задаваме свойството name на "Alice", което е валидно. След това се опитваме да настроим свойството email на празен низ "", който не е валиден и извежда TypeScript грешка по време на компилиране.

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

Заключение

Има много причини, поради които Typescript се превърна в език за преминаване към всеки JavaScript проект. Позволява на разработчиците да откриват проблемите по-рано и с малко предварителни думи помагат на разработчиците да дефинират своите интерфейси, с които работят. Освен това улеснява включването на разработчици с BE опит като Java и C++, тъй като споменатите разработчици вече имат опит с много от функциите, които Typescript може да предложи.