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',
} // расширить тип можно так