Открыть меню    

Typescript

Более подробно на caйте dnzl.ru: TypeScript основы

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> // обозначение дженерика

: [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

Функции и TS

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

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

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

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

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

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

О том, что функция ничего не возвращает можно указать при помощи ключевого слова void:


function greetMe(name: string, greeting: string): void {
    console.log(greeting + name);
}

Обозначение типов функций делается на основе 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

Классы

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

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

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

Отличная статья по поводу классов и интерфейсов в typescript: Classes vs Interfaces от James Henry.

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

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

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
})
interface ITodo {
    title: string;
    completed: boolean;
}

todo: ITodo

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

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

export class ListArticles implements OnInit {
    //..

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

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

Типы в уловых скобках (дженерики)

Иногда мы вынуждены писать тип 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);


// =============================================


// Дженерик может принимать только 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/docsReference/Global_Objects/String/endsWith

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

Комментарии к статье