TypeScript основы

Mar 4, 2018

tsconfig.json - конфигурационный файл для TS

Возможные опции:

"target": "es5"             /* ts будет компилироваться в es5 */
"module": "commonjs"        /* формат import и export */
"strict": true              /* strict режим */
"sourceMap": true           /* ts будет генерировать .map файл, который понимают браузеры */
"noEmitOnError" : false     /* ts не будет генерить js, если в ts есть ошибки */
tsc index.ts
// компилируем index.ts в index.js
// команда tsc читает текущий конфиг tsconfig.json

Типы

Все что приведено ниже ставится после объявления переменной:

: string
: number
: boolean
: any
: string | number
: number | null

// Типы массивов

: number[]
: string[]

// OR

: Array<number> // обозначение дженерика

// С использованием интерфейса и дженерика:
let arr: Array<number>

: [number, number, string]


let a: number;
let b: boolean;
let c: string;
let d: any;
let e: number[] = [1,2,3,4,5];
let f: any[] = [1, true, 'a', false];

Несколько примеров:

С помощью enum мы можем создавать константы и затем использовать их как тип.

// enum

const ColorRed = 0;
const ColorGreen = 1;
const ColorBlue = 2;

enum Color {
    Red,
    Green,
    Blue
};
let backgroundColor = Color.Red; // 0

// or
enum Color {
    Red = 0,
    Green = 1,
    Blue = 2,
    Purple = 3
}

console.log(Color.Purple)      // 3

Псевдонимы типов (type)

TypeScript позволяет определять псевдонимы типов с помощью ключевого слова type. Затем мысможем использовать псевдоним аналогично типу данных.

type strOrNumType = number | string;
let sum: strOrNumType = 3;
if (typeof sum === "number") {
    console.log(sum / 6);
}

Функции в TypeScript

Функции мы можем записывать в двух интерпретациях:

let fun: (a: string, b: number) => string;

или так как функциия это объект, то мы можем использовать объект с безымянным методом

let fun: { (a: string, b: number) => string };

Первый пример создан на основе ES6 синтаксиса. Еще несколько примеров:

type User = {
    name: string,
    age: number,
    getJobs: () => string[],
    jobs: string[],
    logName?: (prefix: string) => void  // название аргументов неважно, главное ТИП
};

let user: User = {
    name: 'John',
    age: 21,
    jobs: ['a', 'b'],
    getJobs(): string[] {
        return this.jobs;
    },
    logName(prefix: string): void {
        console.log(prefix + this.name);
    }
};

user.logName('hello ');  //hello John

Определяем параметры

Параметры по умолчанию - после типа переменной указываем его значение по умолчанию.

// greeting: string = 'Hello' 'Hello' как параметр по умолчанию
function greetMe(name: string, greeting: string = 'Hello'): string {
    return greeting + name;
}

Добавим символ ? в конце параметра, тем самым сделаем такой параметр необязательным.

function greetMe(name: string, greeting?: string): string { // : string означает, что возвращает строку
    return greeting ? greeting : 'Hello' + ', ' + name;
}

Или можно использовать непосредственно в шаблоне:

<b> {{currentUser?.nickname}} </b>

Классы

Классы отличаются от интерфейсов тем, что на основе классов мы можем создать объект. То есть в отличие от интерфейсов это не просто описание формы чего-либо.

Интерфейсы могут быть реализованы не только объектами, но и классами. Для этого используется ключевое слово implements.

Имена классов определяются в стиле UpperCamelCase с добавлением суффикса, указывающего на его тип (компонент, директива, фильтр и т.д.)

Абстрактные классы

От абстрактных классов мы не можем делать экземпляров. Абстрактные классы нужны лишь для того, чтобы от них наследовались.

abstract class User {
    name: string;
    year: number = 1982;

    // метод абстрактный, поэтому в классе, например,
    // Employee он может быть реализован со своей логикой
    abstract logInfo(info: string): void;

    getYear(): number {
        return this.year;
    }
}

class Employee extends User {
    logInfo(info: string): void {
        console.log(info);
    }
}

const john = new Employee();
console.log(john);

john.logInfo('Info');           // info
console.log(john.getYear());    // 1982

Интерфейс

Интерфейс - описание формы объекта, в котором можно описать как обязательные так и необязательные поля.

Можно описать тип так:

let drawPoint = (point: { x: number, y: number }) => {
    //..
}

drawPoint({
    x: 1,
    y: 2
})

Но, как вы видите, это решение актуально для лишь конкретной функции (drawPoint). Обойти эту проблему поможет использование интерфейсов.

interface Point {
    x: number,
    y: number
}

let drawPoint = (point: Point) => {
    //..
}

drawPoint({
    x: 1,
    y: 2
})


// еще 1 пример

interface ITodo {
    title: string;
    completed: boolean;
}

todo: ITodo

При работе с классами интерфейсы определяют контракт (implements) указывающий, что классы реализующие этот интерфейс должны определить определенные свойства и методы.

Например, интерфейс OnInit:

export class ListArticles implements OnInit {
    //..

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

// Привести пустой объект к типу интерфейса Task
this.Task = <Task>{ };

Наследование интерфейсов

Интрефейсы в отличие от type можно наследовать:

interface IMove {
    move(): void;   // как видите для описания функции мы можем использовать 2 сигнатуры
    getCount: () => { count: number };
}

interface IShake {
    shake(): void;
    getCount: () => { frequency: number }
}

interface IMoveShake extends IShake, IMove {
    getCount: () => { count: number, frequency: number }
}

Дженерики (обобщение)

Обобщать можно функции, классы и т.д.

interface HashMap<T> = { [key: string]: T; };

let hashMap<number> = {
    digit: 12
}

let hashMap<number | string> = {
    digit: 12,
    name: 'Vasya'
}

// вместо T может быть любая буква

Интерфейсы описаны в библиотеке lib.d.ts - там вы можете посмотреть как используются дженерики для интерфейсов, например, Array<T>, у которого описан map<U> и не только:

[1,2,3,4].map<string>((val: number) => val.toFixed());

// lib.es6.d.ts
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];

Еще один пример использования дженериков:

interface IA<T extends { id: number, name: string }> {
    prop: T
}

let b: IA<{ id: number, name: string, male: false }>;

Иногда мы вынуждены писать тип any, так, например, в ситуации, когда мы не знаем какой тип придет в функцию. А в typescript вся идеологияя построена на том, что мы знаем типы. Дженерики - обозначение типов в общем виде, которые помогают избежать вышеописанных ситуаций.

Разберем примеры:

// здесь <T> дженерик, который будет использоваться в ф-и
// само T это сокращение от Type

function genericGetter<T>(data: T): T {
    return data;
}

// Дженерик самостоятельно определяет тип данных:
console.log(genericGetter('Test').length);

let newGenericFunction: <T>(data: T) => T = genericGetter; // <T>(data: T) => T это тип

// Самостоятельно указываем тип данных дженерика:
console.log(genericGetter<string>('Test').length);



// 2-й пример:

// Дженерик может принимать только number или string
class Multiply <T extends number | string> {
    constructor(private a: T, private b: T) {}

    public getResult(): number {
        return +this.a * +this.b;
    }
}

const mNum = new Multiply<number>(10, 5);
console.log('Number: ', mNum.getResult());

const mStr = new Multiply<string>('50', '60');
console.log('String: ', mStr.getResult());
getAll(): Promise<Task[]> {
    return tasksPromise;
}

Возвращаемый тип данных - Promise, но при этом с помощью угловых скобок <Task[]> мы указали, что именно в себе хранит этот Promise. Когда мы делаем любую асинхронную операцию, которая возвращает Promise, мы понимаем, что в Promise хранится значение, которое появится через какое-то время. С помощью угловых скообок мы сообщаем это значение. В данном случае Promise будет содержать массив задач. <Task[]>

Ниже мы приводим пустой объект к типу интерфейса Task

//Привести пустой объект к типу интерфейса Task
this.Task = <Task>{ };
// определение массива чисел
let list1: number[] = [1, 2, 3];
// определение массива чисел с использования generic типа Array<elemType>
let list2: Array<number> = [1, 2, 3];


// phrase.ts
export class Phrase {
    value: string;
    language: string;
}

import { Phrase } from "./phrase";
// определяем массив Phrase
let arrPhrase: Phrase[] = [
    { value: "Hello World", language: "English" }
]

assertions (утверждение типа)

assertions бывают двух видов - <> или as

let message; // by default type any
message = 'abc';

// Метод endsWith() определяет, заканчивается ли строка символами другой строки, возвращая, соотвественно, true или false.
// https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith

// принудительно указывает тип для переменной message
let endWithC = (<string>message).endsWith('c');
let alternativeEndWithC = (message as string).endsWith('c');

Как задать объект с произвольным кол-вом свойств?

let hashMap: {
    [key: string]: number|string
} = {}

// key - незарезервированное, может быть любое слово

Модификатор readonly

let account: {
    readonly age:number,
    name: string
} = {
    age: 35,
    name: 'John'
}

account.age = 44; // ошибка
Добавить комментарий
Комментарии:
стас
May 18, 2018
Отличная статья
sdf
May 6, 2018
sdf