Typescript

Сначала установка. Загружаем и устанавливаем node.js, далее в командной строке пишем npm install -g typescipt, выполняется автоматическая установка.
Теперь можно создавать проект. Делаем index.html, в теге script указываем ему app.js. Затем создаём собственно сам typescript-файл app.ts. После завершения разработки компилируем его в js: в командной строке пишем tsc PATH/TO/PROJECT/app.ts. На выходе получается нужный нам app.js

Компилировать можно в разные версии EcmaScript, для этого есть параметр -target: tsc PATH/TO/PROJECT/app.ts -target ES2017.

Задаём папку для хранение компилированных файлов: tsc –outDir PATH/TO/PROJECT/JS PATH/TO/PROJECT/app.ts -target ES2017.

Объединяем несколько файлов в один: tsc –outFile PATH/TO/PROJECT/output.js PATH/TO/PROJECT/app1.ts PATH/TO/PROJECT/app2.ts.

Программирование

В TS больше типов данных, чем в JS, а также три вида переменных:

var x = 1; // как в JS
let y = 1; // явно не переназначается
let y = 2; // возникнет ошибка, будет выведена на стадии компиляции
y = 8; // но переопределяется в коде; ошибки не будет
let y = 10; // переопределять переменную с другим типом тоже нельзя, будет ошибка
const z = 11; // константа, определяется один раз и значение больше не меняется
z = 12; // ошибка

Самое важное в типах данных в TS заключается в том, что они не меняются после определения:

let firstName: string = "Вася";
firstName = 23; // ошибка, изменили тип данных со String на Number

Типы присваиваются не только явно, как выше, но и по умолчанию:

let isTrue = false;
let list = [1,2,3];

Поддерживаются следующие типы данных:
– string;
– number;
– boolean;
– array;
– object;
– null;
– undefined;
– tuple (кортежи);
– enum (перечисления);
– any (любой тип);
– unknown (то же, что any, но с явным указанием, что тип именно неизвестен, а не любой):
– void (тип возвращаемого функцией значения, когда она не возвращает ничего или undefined);
– never (указывает, что функция точно ничего не вернёт, даже undefined);

Например, с типом any следующая последовательность пройдёт:

let firstName: any = "Вася";
firstName = "Петя";
firstName = 23;

Строки string поддерживают шаблоны:

let firstName: string = "Вася";
let age: number = 23;
let info: string = `Имя: ${firstName}, Возраст: ${age}`; // шаблоны в косых кавычках
console.log(info);  // Выведет в консоли следующее: "Имя: Вася, Возраст: 23"

Типизация работает и для объектов. Если объявили объект с двумя полями, нельзя затем переопределить его с одним:

let person = {
   name: "Вася",
   age: 23
};
person = {age: 24};  // ошибка, не хватает второго поля
person = {age: 24, name: "Петя"};  // ошибки нет, последовательность полей на важна

Можно задавать более одного предполагаемого типа переменной:

let names: string | string[] | number;
names = "Вася";
names = ["Катя", "Лена"];
names = 12;
names = [24,48];  // здесь возникнет ошибка, т.к. массив предполагается только со строками

Для сокращения записи можно пользоваться псевдонимами типов через ключевое слово type:

type onetwo = number | string;
let firstName: onetwo = "Костя";
firstName = 46;  // ошибки нет, так как тип, выраженный через псевдоним, предполагает два значения
firstName = true;  // тут уже ошибка

Преобразование типов к определённому виду (type assertion):

let someValue: any = "Привет!";
let valueLength: number = (<string>someValue).length;  // указание типа через стрелочные скобки
console.log(valueLength);  // выведет 7
let someOtherValue: string | number = "Привет, медвед!";
let otherValueLength: number = (someOtherValue as string).length;  // второй вид указания типа
console.log(otherValueLength);  // выведет 15
let someValue: any = "Привет!";
let valueLength: number = (<string>someValue).length;  // указание типа через стрелочные скобки
console.log(valueLength);  // выведет 7
let someOtherValue: string | number = "Привет, медвед!";
let otherValueLength: number = (someOtherValue as string).length;  // второй вид указания типа
console.log(otherValueLength);  // выведет 15

Несколько типов переменной можно задать двумя путями: union и generic types:

const m: string | number = 2;  // union - способ перечислить возможные типы через знак |
function identity(opt: T): T => {  // аргумент при каждом вызове функции определяет возвращаемый тип, таким образом, она может работать с разными типами
   return opt;
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
   return obj[key];
}
const thing = { type: 'casual' };
getProperty(thing, 'type');  // вернёт 'casual'; T и K передаются в момент вызова функции, причём K находится в зависимости от T

Функции

Для функций в TS можно определить типы входных параметров, тип возвращаемого значения:

function(a: number, b: number, param?: string): string {};  // последний string после двоеточия указывает на тип возвращаемого значения, знак вопроса перед параметром указывает на его необязательность

Пример:

let f = function(a: number, b: number, param?: string): string {
   let res = a+b;
   let ress: string = String(res);
   ress = "Результат " + param + " равен " + ress;
   return ress;
}
var m = f(1,2, "сложения");
console.log(m);  // выведет "Результат сложения равен 3"
m = f(3,5);
console.log(m)  // выведет "Результат undefined равен 8"
m = f(5);  // ошибка, обязательный параметр пропущен

Функции в TS также предполагают значения параметров по умолчанию:

let f = function(a: number, b: number, param: string="действия"): string {
   let res = a+b;
   let ress: string = String(res);
   ress = "Результат " + param + " равен " + ress;
   return ress;
}
var m = f(1,2);
console.log(m);  // выведет "Результат действия равен 3"

Можно также указать на набор однотипных параметров посредством многоточия, чтобы не перечислять все возможные:

function addNumbers(firstNumber: number, ...numberArray: number[]): number {     
    var result = firstNumber;
    for (var i = 0; i < numberArray.length; i++) {
        result+= numberArray[i];
    }
    return result;
}
 
var result1 = addNumbers(3, 7, 8);
console.log(result1);  // выведет 18 
var result2 = addNumbers(3, 7, 8, 9, 4);
console.log(result2);  // выведет 31
var result3 = addNumbers(1);
console.log(result3);  // выведет 1
var result4 = addNumbers(1, "Вася");  // ошибка, не соблюдён тип

В TS можно переопределять функции для разных типов параметров:

function add(x: string, y: string): string;
function add(x: number, y: number): number;
function add(x: any, y: any): any {
   return x + y;
}
 
var result1 = add(5, 4);
console.log(result1);   // выведет 9; был вызван второй вариант функции
var result2 = add("5", "4");
console.log(result2);   // выведет "54", т.к. строки складываются последовательно значениями; был вызван первый вариант функции

Функции в TS могут записываться в виде лямбда-выражений:

let sum = (x: number, y: number) => {
   x *= 2;
   return x + y;
}; 
let result = sum(15, 35); 
console.log(result);  // выведет 65

Лямбда-выражение можно передавать функции в качестве параметра:

function mathOp(x: number, y: number, operation: (a: number, b: number) => number): number{ 
   let result = operation(x, y);
   return result;
}
console.log(mathOp(10, 20, (x,y)=>x+y));  // выведет 30 
console.log(mathOp(10, 20, (x, y) => x * y));  // выведет 200 

Параметры функции с union-типами обрабатываются методами, которые существуют для всех типов:

function doThis(x: string | number) { 
   const res1 = x.toLowerCase();  // здесь ошибка, т.к. для number не существует метода toLowerCase();
   const res2 = '1' + x; // здесь всё хорошо, при сложении тип number может приводиться к строке
}

Но аргументы с union-типами можно проверять внутри функции:

function doThis(x: string | number) {
  const res1 = undefined;
  if (typeof x === 'string') {
    return x.toLowerCase();   
  } else {
     return '1' + x;
  }
};

Модули

TS поддерживает модули, которые представляют собой каждый свою область видимости.

const someFunc() {...}
export {someFunc}
export {someFunc as sf} // можно переименовать для удобства

Можно также экспортировать все экспортируемые сущности из разных источников:

// файл экспортирует содержимое соседних
export * from './helper.ts';
export * from './validatior.ts;
 
// на примере одного из файлов: экспортируется то, что предназначено для экспорта, функция _check не экспортируется
export helperFunction() {...}
function _check() {...}

Импорт аналогично:

// расширение можно не указывать
import * as imported from "../exporting-modules"
let help = imported.helperFunction;

Типы и интерфейсы

В TS существует два похожих способа указать типизацию для набора полей – с помощью типов или интерфейсов:

type Bike = {
   model: string;
   horsepower: string;
   secondPassengerWeight?: number;
}
interface Bike {
   model: string;
   horsepower: string;
   secondPassengerWeight?: number;
}

Главное различие между ними – интерфейсы можно расширять, типы нет. Typescript Handbook рекомендует использовать интерфейсы по умолчанию, типы только в тот момент, когда вам потребуются их особенности:


interface Bike {
   model: string;
}
interface Bike {
   horsepower: string;
}
const Fireblade: Bike = {
   model: '929',
   horsepower: '136',
} // всё нормально, такое расширение интерфейса допустимо
type Bike = {
   model: string;
}
type Bike = {
   horsepower: string;
} // ошибка
type Bike = {
   model: string;
}
const Fireblade: Bike & { horsepower: string; } = {
   model: '929',
   horsepower: '136',
} // расширить тип можно так
Теги: