TypeScript - JavaScript тільки з типами, якось так коротко можна це описати щоб всі зрозуміли.

TypeScript - може як прискорювати роботу так і уповільнювати її в залежності від правильності використання, приклади:

  1. При типізації API - це явно прискорюватиме роботу як для людей які уже працюють над проєктом, так і для людей які будуть над ним працювати. [USEFULL]

  2. Типізація все-можливих подій по типу клік на елемент який знаходиться всередині іншого елементу лише для того щоб взяти властивість висоти елементу - крінж. [USELESS]

Нижній Текст
У цій статті будуть певні приклади коду та техніки використання можливостей TypeScript які спростять та збільшать швидкість написання коду.

Примітиви

Загальні типи

Документація говорить що є всього 3 примітивних типи:

type Primitive = string | boolean | number;

Можна також додати сюди symbol - 99% випадків вам це не знадобиться.

Також можна розширити і додати undefined, null, але це не правильно, так як у undefined, null - немає ніяких методів. Краще буде зробити окремий тип Nullable і використовувати оператор “?” - для випадків з undefined;

type Nullable<T> = T | null;

type Primitive = string | boolean | number | symbol | null | undefined;

type DTO = { field: Primitive };


type Primitive1 = string | boolean | number | symbol;

type DTO1 = { field?: Nullable<Primitive> };

При роботі із першим типом ви завжди будете отримувати помилку - “'.field' is possibly 'null' or 'undefined'.”

При роботі з другим варіантом ви будете отримувати ці повідомлення тільки якщо обернете у Nullable або поле буду опціональним, це дасть вам більшу гнучкість при типізації.

Оверхед

При роботі з примітивами супер-типізація може згубно впливати на швидкість написання коду.

Потрібно враховувати межу коли це - “Упоротий" або “Валідний" код.

приклад:

const [count, setCount] = useState<number>(0); // OK
const [count, setCount] = useState<0 | 1 | 2>(0); // not OK

У другому випадку може бути валідно тільки у якщо не потрібно щоб count був щось окрім “0, 1, 2", але краще це опрацювати на рівні своєї сет-функції

const handleUpdateCounter = (newCount: number) => {
  if(newCount > 2) return;
  setCount(newCount);
}

Місце де можна використати щось типу такого - це статуси або конкретні числові або текстові значення.

type USER_STATUS_TYPE = "REGISTERED" | "VERIFIED" | "BLOCKED";

але також потрібно враховувати те що вам потрібно буде використовувати значення цього типу для перетворення або порівняння, тоді краще використати enum.

enum USER_STATUS_ENUM {
  REGISTERED = "REGISTERED",
  VERIFIED = "VERIFIED",
  BLOCKED = "BLOCKED"
}

const userStatus: USER_STATUS_ENUM = ...;

для складних операцій із подібними типами, або для використання такого роду типів за межами рендеру - краще обрати об’єкти.

const USER_STATUS_ENUM = {
  REGISTERED: "REGISTERED",
  VERIFIED: "VERIFIED",
  BLOCKED: "BLOCKED"
}

type UserStatus = keyof typeof USER_STATUS_ENUM;

const isValidType = (userStatus: UserStatus) => Object.values(USER_STATUS_ENUM).includes(userStatus);
Звісно при грамотній типізації цей кейс вам не доведеться використовувати, але якщо текстові данні потрапляють із форми яку ввів користувач, або ви дуже не довіряєте API то додати подібну перевірку - зайвим не буде.

Шаблонні рядки

Це один із видів заплутування розробників, який у більшості випадків використовується людьми які знають що їм потрібно.

type StringUrlPattern = `http://${string}.${string}`;
const url: StringUrlPattern = 'http://google.com' // - correct;
const url1: StringUrlPattern = 'http://google' // - error;

Крута штука, яку варто знати, але також це може викликати багато проблем:

  1. Уповільнює рефакторинг

  2. Без коментарів новим розробникам ваші рішення можуть бути не очевидними.

  3. При зав’язуванні всієї логіки певного модуля на таких типах, його розширення - це дуже складна задача, бо новий функціонал не завжди ідеально натягується на старий.

type StringUrlPattern = `http://${string}.${string}`;
const url: StringUrlPattern = 'http://google.com' // - correct;
const url1: StringUrlPattern = 'http://google' // - error;

const url2: StringUrlPattern = 'http://google.com.com.com' // - correct;
const url3: StringUrlPattern = 'http://..' // - correct;

Ось які проблеми можуть чекати на вас якщо довіряти лише типам, це не очевидні речі, які можуть викликати додаткову купу функцій-валідацій, які у свою чергу перекриють всю ідею шаблонних рядків.

Враховуйте ці пункти при використанні шаблонних рядків.

Допоміжні типи

type Str1 = Uppercase<"text">; // TEXT

type Str2 = Lowercase<"TEXT">; // text

type Str3 = Capitalize<"text">; // Text

type Str4 = Uncapitalize<"Text"> // text;

type Str5<S extends string = 'TEST'> = `${Lowercase<S>}-%{S}; // test-TEST

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

never | unknown | any

any

any - будь-яке значення. Але правильніше говорити що any - просто ігнорує типи, а не є чим завгодно. Можливі різні комбінації:

type Type = any;
type Type1 = any[];
type Type1 = Record<string, any>;

const entity = {} as any;

never

never - ніколи. Це означає що цього значення ніколи не може бути, найчастіше використовується у функціях щоб сказати що ця функція ніколи нічого не повертає, якщо ви напишете return у такій функції - отримаєте помилку.

Лайфхак:

const USER_STATUS_ENUM = {
  REGISTERED: "REGISTERED",
  VERIFIED: "VERIFIED",
  BLOCKED: "BLOCKED",
}

type Status = keyof typeof USER_STATUS_ENUM;

function assertNever(value: never): never {
  throw new Error(`Unexpected value: ${value}`);
}


function getStatusLabel(status: Status) {
  switch (status) {
    case "BLOCKED":
      return 'Block';
    case "VERIFIED":
      return 'Verify';
    case "REGISTERED":
      return 'Register';
    default:
      assertNever(status);  // If you forgot update 'cases', TypeScript will show compile-time error here.
  }
}

Якщо додати ще одне поле у об’єкт USER_STATUS_ENUM - ви отримаєте помилку “Argument of type 'string' is not assignable to parameter of type 'never'.” - за допомогою таких маленьких лайфхаків можна зекономити собі багато часу при розширенні коду.

unknown

unknown - менше зло ніж any але теж маленьке зло. Цей тип використовується коли ви хочете позначити щось, але не знаєте що там, у той час як any - відключає типізацію, unknown - говорить що тут буде якась значення, але поки не ясно яке, при роботі із unknown його потрібно переприсвоювати.

type Response = { name: string };
const a = apiResponse as unknown as Response;

так краще не робити, але це приклад того як можна використати unknown.

const data: unknown = getValueFromExternalLibraryWithoutTypes();

у таких випадках unknown - це найкращий тип який можна використати.

const data: unknown = getValueFromExternalLibraryWithoutTypes();
let localData: string;

localData = data; // Error;

якби тут ми використали any - була б біда.

Підсумки

Лише з примітивами можна значно покращити “DX” команди при роботі з TypeScript.

Деякі із цих речей очевидні і можуть здаватись занадто простими, але про них потрібно пам’ятати щоб використовувати всі можливості TypeScript.

На цьому завершу. Розкрити всі теми і лайфхаки TypeScript у одному довгочиті дуже складно, і це буде не інформативно, тому у наступних статтях буде більше цікавих можливостей TypeScript.

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Valentyn Tupota
Valentyn Tupota@valentyn_tupota

Software Engineer

1.3KПрочитань
3Автори
37Читачі
На Друкарні з 11 жовтня

Більше від автора

  • Neural Network [guide] 1

    Все починається із першого нейрону. Алгоритм лінійної регресії для одного нейрону, та принципи його використання.

    Теми цього довгочиту:

    It
  • Frontend [TypeScript] 2

    TypeScript - Як писати код швидше та надійніше. Про неочевидні речі.

    Теми цього довгочиту:

    It
  • [Frontend] Універсальні рішення

    Потряпляючи у пастки антипатернів велику кількість разів ви навчитесь писати гарні “універсальні рішення".

    Теми цього довгочиту:

    It

Вам також сподобається

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається