React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
TypeScript. Начало работы

Основная идея TypeScript как будто понятна: мы указываем типы переменных и параметров, а инструмент следит, чтобы эти типы соблюдались. Если попробовать скомпилировать код с ошибками, в консоли будут подробные сообщений - где и что не так.

Тип указывается через двоеточие:


Для переменной:


const numberVariable: number = 42;
const stringVariable: string = 'Hello';


Для входных параметров функции:


function sum(a:number, b:number) {

}


Для выходного значения функции:


function sum(a,b): number {

}


#typescript
👍3
Forwarded from Cat in Web
JavaScript vs TypeScript. Почему Вы должны изучить TypeScript?

Статья (рус.): https://habr.com/ru/post/660791/

TypeScript контролирует типы данных в вашем коде (переменных, параметров функции) и предотвращает множество ошибок, связанных с несовместимостью типов. Он предлагает статическую типизацию вместо динамической (JavaScript).

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

🔹 Примитивные типы данных: string, number, boolean, null, undefined
🔹 Магический тип данных any и нужен ли он
🔹 Автоматическое определение типа данных при создании переменных
🔹 Интерфейсы для определения объектов
🔹 Типы данных для массивов (два способа указания)
🔹 Представление о дженериках (понятно, как работают, не понятно, в каких ситуациях использовать)
🔹 Объединение (union) - возможность указать несколько типов на выбор
🔹 Кортеж (tuple) - набор элементов с разными типами

#typescript #junior
👍4
TypeScript: Раскладываем tsconfig по полочкам

Разберемся в основных настройках TypeScript.

Статьи (рус.):
Часть 1
Часть 2

Исходные и конечные файлы

files, include, exclude - принимают массивы с именам файлов (или шаблонами имен).

compilerOptions.allowJs, compilerOptions.checkJS - работа с JS-файлами (например, при миграции проекта с JS на TS).

compilerOptions.outDir, compilerOptions.outFile - директория и имя файла для итоговой сборки.

compilerOptions.resolveJsonModule - можно импортировать JSON-файлы.

jsx - работа с JSX-разметкой (в React).

Работа с модулями

compilerOptions.module - система модулей, которую будет использовать скомпилированное приложение. По умолчанию commonjs (exports, require), можно поставить 'es6' для фронтенд-проектов.

compilerOptions.moduleResolution - стратегия импорта модулей, в подавляющем большинстве случаев значение параметра равно node.

compilerOptions.esModuleInterop - позволяет импортировать CommonJs-пакеты в стиле ES6.

compilerOptions.forceConsistentCasingInFileNames - режим чувствительности к регистру импортируемых файлов.

Использумая функциональность

compilerOptions.target - версия стандарта JS, в которую компилируется исходный код. По умолчанию (в сгенерированном файле) стоит es2016, который поддерживается большинством браузеров.

compilerOptions.compilerOptions.lib - сторонняя функциональность, которая используется в проекте (полифиллы). По умолчанию для target: es6 подключаются методы стандарта ES6, а также DOM, DOM.Iterable, ScriptHost.

Строгость

compilerOptions.strict - включает строгий режим JS и ряд строгих проверок (на использование типа any, неявного this и т.д.). Можно заменить набором отдельных настроек.

Линтинг

compilerOptions.noImplicitReturns, compilerOptions.noUnusedParameters, compilerOptions.allowUnreachableCode, compilerOptions.removeComments и тому подобное.

Компиляция

compilerOptions.noEmitOnError - не прерывать компиляцию при ошибке.

#typescript #подключение
👍1
TypeScript. Типы данных

Типы, знакомые из JavaScript:

- string
- number
- boolean
- null
- undefined
- object

Особые типы:

- void (для функций, которые ничего не возвращают)
- never (для функций, которые никогда не завершаются)

Еще есть массивы, кортежи, перечисления и функции (о них в следующих постах)

#typescript
👏2👍1
TypeScript. Массивоподобные типы

1. Массив, все элементы которого имеют одинаковый тип данных (массив чисел, массив строк)

Есть два способа определить массив:

- указываем тип данных, а после него квадратные скобки
- указываем конструктор Array, а после него в угловых скобках тип. Такая запись называется дженерик, вернемся к ним позже.

2. Массив с известным числом и порядком элементов разных типов (кортеж)

- в квадратных скобках перечисляются типы всех элементов по порядку

#typescript
👍2🔥2
TypeScript. Перечисления (enums)

Видео (рус.): https://www.youtube.com/watch?v=FltLrtKWMak

Перечисление, или enum, - это способ создать коллекцию ключей, каждому из которых будет сопоставлено уникальное значение.


enum Directions {
Up,
Down,
Left,
Right,
}


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

Up, Down, Left, Right - это как бы названия свойств коллекции.


Directions.Up // 0
Directions.Down // 1


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

Если дефолтный порядок нумерации не устраивает, можно установить любые кастомные индексы:


enum Directions {
Up = 2,
Down = 4,
Left = 6,
Right = 8,
}


Можно, наоборот, получать элементы по индексу:


Directions[6] // 'Left'


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


enum Links {
vk = 'https://vk.com/',
youtube = 'https://youtube.com'/,
facebook = 'https://facebook.com/'
}


В видео чуть поглубже показано подкапотное поведение enum. В частности, про то, что enum компилируется в вызов функции (IIFO), создающий объект с коллекцией. То есть объект создается в любом случае, даже если не используется.

Но если добавить к объявлению перечисления слово const, то объект не будет создан, если к нему нет ни одного обращения в дальнейшем.

#typescript
👍3
TypeScript. Аргументы функций

#typescript
2🔥1
TypeScript. Функция как тип

Как описать переменную, если в ней должна лежать функция? Как ни странно, с помощью стрелочной функции.

#typescript
👍1🔥1
TypeScript. Кастомные типы данных

Можно создавать собственные типы данных с помощью ключевого слова type:


type Name = string;
const userName1: Name = 'John';
const userName2: Name = 'Jane';


Таким же образом можно создавать сложные типы (объекты):


type Person = {
name: string,
age: number,
nickName?: string
}


Свойства со знаками вопроса - необязательные.

Такой тип можно переиспользовать много раз:


const user1: Person = { name: 'John', age: 25, nickName: 'Zorro' };
const user2: Person = { name: 'Jane', age: 23 };


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


const user3: { name: string, age: number, nickName?: string } = { name: 'Robert', age: 30 };


Важно: если присвоить в переменную объект, у которого будут "лишние" свойства (не описанные в типе), TypeScript выбросит ошибку.

#typescript
👍1🔥1
TypeScript. Классы, свойства классов

Классы в TS определяются точно так же, как в JS:

class User {

}


В TS мы можем объявить поля класса и указать их типы:

class User {

name: string;
age: number = 20;
nickName: string = 'Superman';
private secret = '12345';

constructor(name: string, age: number, nickName: string) {
this.name = name;
this.age = age;
this.nickName = nickName;
}

}


Есть модификаторы доступа:
- public (по умолчанию все свойства публичные)
- private (доступны только внутри текущего класса)
- protected (доступны внутри текущего класса и его подклассов)
- readonly

Также свойствам можно указывать значения по умолчанию.

Чтобы не дублировать код свойств три раза (определение в классе, параметры конструктора и присвоение в конструкторе), можно определять их сразу в конструкторе:

class User {
constructor(
public name: string,
public age: number,
public nickName: string
) {

}
}


Тут обязательно указывать модификаторы для каждого свойства. Все свойства, указанные таким образом автоматически присвоятся одноименным полям класса.

#typescript
👍1🔥1
TypeScript. Классы, статические свойства

Для объявления статических свойств (принадлежащих классу, а не экземпляру), используется ключевое слово static:

class User {
static secret = 12345;
}


Это то же самое, что и

class User {}
User.secret = 12345;


#typescript
👍1🔥1
TypeScript. Абстрактные классы

В TypeScript есть возможность создавать абстрактные классы - концепция из сферы полиморфизма, обеспечивающая общий интерфейс для обычных классов, наследующих от абстрактного. При этом экземпляры абстрактного класса напрямую создавать нельзя.

abstract class User {
constructor(
public name: string,
public age: number
) {}

greet(): void {
console.log(this.name);
}

abstract getPass(): string;
}


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

#typescript
👍1🔥1
TypeScript. Интерфейсы

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

interface User {
name: string,
age?: number
}


Это похоже на простое создание кастомного типа:

type User = {
name: string,
age: number
}


И используется интерфейс так же, как обычный тип:

const john: User = {
name: 'John',
age: 30
}


Но интерфейс - более мощная концепция. Если type это просто создает псевдоним для типа, то интерфейс - это именованный тип объекта, который может наследоваться и расширяться другими интерфейсами.

В интерфейсах также доступен модификатор readonly:

interface User {
readonly name: string,
}


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

interface User {
name: string,
age: number,
[propName: string]: any
}


Интерфейсы могут описывать даже классы (похоже на абстрактный класс https://yangx.top/react_junior/401):

interface User {
name: string,
age: number,
getPass(): string
}


Чтобы реализовать этот интерфейс, заинтересованный класс использует ключевое слово implements:

class Admin implements User {
name: string,
age: number,

nickName: string,

getPass() {
return `${this.name}${this.age}`;
}
}


В классе при этом могут быть и дополнительные поля и методы, интерфейс лишь определяет минимальный набор полей.

Класс может реализовывать сразу несколько интерфейсов (перечисляются через запятую):

interface User {
name: string,
age: number
}

interface Pass {
getPass(): string,
}

class Admin implements User, Pass {
name: string,
age: number,

getPass() {
return `${this.name}${this.age}`;
}
}


А сами интерфейсы могут наследовать от других интерфейсов:

interface Admin extends User {
getPass(): string,
}


#typescript
👍1🔥1
TypeScript. Дженерики

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

const foo = function(data: any): any {
return data;
}

С точки зрения TS функция валидная, однако компилятор не может определить, с каким типом данных он работает, поэтому возможны ошибки, которые он не сможет поймать:

foo(10).length;

Чтобы вернуть контроль, нам нужно точнее указать тип. Как минимум, показать, что на выходе будет то же самое, что и на входе - один и тот же тип. Для этого в TS есть дженерики (обобщения).

Дженерик - это способ обозначить тип, не называя его напрямую. Обычно для обозначения типа используют букву T (type), но можно взять и другие буквы.

#typescript #дженерики
👍1🔥1
Дженерики

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

Когда мы вызываем функцию с параметром 10, TypeScript берет тип этого параметра и считает, что T теперь равно этому типу. Теперь компилятор может определить, что у выходного значения нет свойства length, и выведет ошибку.

Или мы можем вручную указать тип при вызове функции (в угловых скобках перед списком параметров.

#typescript #дженерики
👍2🔥1
TypeScript. Дженерики в классах

Дженерики можно использовать и в классах, нужные типы при этом указываются в угловых скобках сразу после имени класса. Типов может быть несколько, необязательно один.

При создании экземпляра конкретные типы (вместо заместителей) выведутся из входных параметров.

#typescript #дженерики
👍1🔥1
TypeScript. Декораторы

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

Грубо говоря, у нас есть некоторая функция, которая умеет что-то делать, например, складывает два числа:

function add(a, b) {
return a + b;
}

Мы хотим добавить для нее логирование - фиксацию каждого вызова с указанием его параметров.
Пишем простейший декоратор для этого:

function logger(fn) {
return function(...params) {
console.log('Вызов функции', ...params); // регистрирует вызов
return fn(...params); // вызывает основную функцию
}
}

Теперь завернем функцию в декоратор и получим декорированную функцию, которая делает то же самое, что и наша основная функция add, но также имеет дополнительную функциональность - логирует свои вызовы.

const addWithLogs = logger(add);
addWithLogs(4, 5); // 9

В этот декоратор можно завернуть и любую другую функцию.

Получается, что у нас тут есть основная функция add, дополнительная функциональность (вызов console.log()) и обертка над всем этим, которая принимает функцию, а затем вызывает ее, не забывая передать параметры. По сути вот так:

function decorate(fn, decorator) {
return function decoratedFn(...params) {
decorator(...params);
return fn(...params);
}
}

function logger(...params) {
console.log('Вызов функции', ...params);
}

function add(a, b) {
return a + b;
}

const addWithLogs = decorate(add, logger);

TypeScript предлагает удобную реализацию этого паттерна, инкапсулируя всю эту обертку. Правда работают они только с классами - можно декорировать сами классы, а также их свойства и методы, для обычных функций эта фича не поддерживается.

Декораторы в TS - это экспериментальная функция, поэтому для поддержки нужно добавить в tsconfig.json опцию compilerOptions.experimentalDecorators.

#typescript #tsдекораторы
👍1🔥1
TypeScript. Декораторы классов

Самый простой вид декораторов в TS - декораторы классов. Они позволяют добавить новую функциональность целому классу.

Хороший пример есть в документации TS: декоратор sealed, который запрещает расширять прототип класса.

Декоратор класса - это простая функция с единственным аргументом constructor - это собственно конструктор декорируемого класса. Через конструктор декоратор может повлиять на определение класса в целом:

function sealed(constructor: Function) {
console.log('sealed decorator');
Object.seal(constructor);
Object.seal(constructor.prototype);
}

Это мы описали "дополнительную функциональность". Чтобы применить ее к любому классу, нужно сделать вот так:

@sealed
class User {
constructor(public name: string) {}
print(): void {
console.log(this.name);
}
}

Просто пишем @ плюс имя функции-декоратора над объявлением класса User.

Обертка класса в декоратор осуществляется под капотом, а сам класс User уже сразу имеет новую функциональность, добавленную декоратором. Например, мы не можем добавить в него новое свойство:

Object.defineProperty(User, 'age', {
value: 17
});
// Cannot define property age, object is not extensible

В скомпилированном виде это все выглядит весьма громоздко

#typescript #tsдекораторы
👍3🔥1
TypeScript. Декораторы методов класса

Декораторы методов выглядят уже немного сложнее, у них не один, а целых три параметра:

function methodDecorator(
target: any,
propertyName: string,
descriptor: PropertyDescriptor
) {

}

- target - это прототип класса. Для статического метода тут будет находиться функция-конструктор класса.
- propertyName - это название метода.
- descriptor - это дескриптор свойства (метода)

Дескриптор свойства - это объект, описывающий "настройки" свойства. У него есть поля:

- configurable,
- enumerable,
- value
- writable
- get
- set

Таким образом, в декораторе мы можем изменять настройки метода, изменяя его дескриптор. Например, можем сделать метод неизменяемым:

function readable(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.writable = false;
}

Используется он так же, как и декоратор класса, только над методом:

class User {
constructor(public name: string) {}

@readable
print(): void {
console.log(this.name);
}
}

Теперь для экземпляра класса User не получится переопределить метод print:

const john = new User('John');
john.print = function() {
console.log('Method print has been changed');
}
john.print();
// Cannot assign to read only property 'print' of object 'User'

#typescript #tsдекораторы
👍2🔥1
TypeScript. Декораторы параметров метода класса

Задекорировать можно даже параметры метода, хотя это уже идея посложнее.

Декоратор параметра принимает три аргумента:

- target - прототип класса или конструктор (для статических методов)
- propertyKey - название метода
- parameterIndex - порядковый номер параметра

Например, создадим декоратор, который будет логировать некоторые параметры метода.

На самом деле, сам декоратор параметра ничего логировать не будет, так как он не имеет доступа к вызовам метода. Но он может отметить параметры, которые нужно вывести в консоль.

function logParameter(
target: any,
key: string,
index: number
) {
const metadataKey = `__log_${key}_parameters`;
const loggedParams = target[metadataKey];
if (Array.isArray(loggedParams)) {
loggedParams.push(index);
} else {
target[metadataKey] = [index];
}
}

Создаем уникальный ключ для метода и сохраняем массив параметров, которые нужно будет вывести в консоль при вызове.

Теперь напишем декоратор для самого метода. Он переопределит реализацию метода, через его дескриптор (поле value). При вызове метода мы сначала выведем те параметры, которые были декорированы, а затем уже вызовем сам оригинальный метод.

function logMethod(
target: any,
key: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const metadataKey = `__log_${key}_parameters`;
const loggedParams = target[metadataKey];

if (Array.isArray(loggedParams)) {
loggedParams.forEach(function(index) {
const arg = args[index];
console.log(`Метод: ${key}, параметр: ${index}`, arg);
})
}

const result = originalMethod.apply(this, args);
return result;
}
return descriptor;
}

#typescript #tsдекораторы
👍4