Друкарня від WE.UA

Frontend [TypeScript] 1

Тут

Type vs Interface

Як інструменти типізації у багатьох випадках різниця буде непомітною.

Все окрім “implements" - можна реалізувати через “type";

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

interface IUser { name: string; age: number; }

Багато людей використовує наступні правила:

  1. Для об’єктів - інтерфейси

  2. Для всього іншого - типи


interface IUser {
  name: string;
  age: number;
  status: "registered" | "blocked";
}

Такий код добре читається, але якщо замінити “interface" на “type" то великої різниці не буде, професіонали називають це - “вкусовщина".

type Status = "registered" | "blocked";

interface IUser {
  name: string;
  age: number;
  status: Status;
}

Опираючись на правила які описані вище саме так потрібно використовувати взаємодію “type" та “interface"


Type Merging

interface IUser {
  name: string;
}

interface IUser = {
  age: number;
}

const user: IUser = {
  name: 'Jhon',
  age: 27,
} // OK

тепер типи

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

// уже на цій стадії буде помилка (Duplicate identifier 'User'.)

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

Tuples

interface ITuple extends Array<string | number> {
 0: string; 
 1: string; 
 2: number;
}

Виглядає кріпово і незрозуміло - не робіть так.

Варіант для нормальних людей.

type Tuple = [firstName: string; lastName: string; id: number];

При порівнянні 2й варіант виглядає значно краще і читабильніше, звісно можна написати просто:

type Tuple = [string; string; number];

Але такий варіант значно менше інформативний, використовуйте попередній варіант - команда буде вам вдячна.

Я не буду розповідати всі комбінації можливих реалізацій через type чи interface - це тема окремої статті. Тут лише хочу донести головні ідеї які вплинуть на читабельність та швидкість писання коду.


keyof | typeof | in

keyof

Порівнюючи з JavaScript у якому є метод об’єкту Object.keys() у TypeScript маємо keyof який повертає набір [“union"] не індексованих значень об’єкту або масиву (у JS теж є об’єктом).

type EntityType = {
  name: string;
  age: number;
  id: number;
}

type EntityTypeKeys = keyof EntityType; // 'name' | 'age' | 'id';

Тепер можна використовувати EntityTypeKeys як окремий набір значень.

type CapitalizedType = {
  [k in EntityTypeKeys as `${Capitalize<k>}`]: EntityType[k];
}
// { Name: string; Age: number; Id: number; }

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

typeof

якщо вам потрібно перетворити уже створений об’єкт у тип, можна використовувати typeof

const user = { name: 'Json', age: 27 };

type UserType = typeof user;

// { name: string; age: number }

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

Якщо поле об’єкту може бути двох типів то через оператор typeof ми отримаємо не коректний тип.

const user = {
  name: 'Jhon',
  age: 27,
  status: 'registered' // can be number (1, 2, 3)
}

type UserType = typeof user; 
// { name: string; age: number, status: string }

type CorrectUserType = Omit<UserType, 'status'> & { 
  status: string | number;
};

// { name: string; age: number, status: string | number }

in

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

type Employee = {
  name?: string;
  department?: string;
  country?: string;
};

const emp: Employee = {};

// 👇️ (property) department?: string | undefined
emp.department

if (emp.department !== undefined) {
  console.log(emp.department.toLowerCase());
}

if (emp.hasOwnProperty('department')) {
  console.log(emp.department.toLowerCase());
}

// ---

if('department' in emp) {
  console.log(emp.department.toLowerCase());
}

Перший варіант не коректний, він не перевіряє чи є таке поле у об’єкті, насправді він перевіряє чи значення цього поля дорівнює undefined, що не правильно.

Другий варіант правильніший для перевірки існування поля у об’єкті, навіть якщо значення undefined.

Третій варіант краще використовувати для швидкої перевірки існування певного поля у об’єкті, але він не дуже надійний.

const entity = { name: 'Jhon' };

'constructor' in entity    // true
'__proto__' in entity      // true
'hasOwnProperty' in entity // true

// У той час як hasOwnProperty

entity.hasOwnProperty('constructor');    // false
entity.hasOwnProperty('__proto__');      // false
entity.hasOwnProperty('hasOwnProperty'); // false
Потрібно пам’ятати при роботі з подібними назвами полів, для себе я порівнюю роботу “hasOwnProperty” та “in" як різницю між “for of" та “for in" відповідно.“in” варто використовувати якщо ви впевнена у собі людина.

Оператори

& (Intersection)

& - поєднання типів. Щось типу впевненої у собі кон’юнкції.

type A = B & C;
B, C - можуть бути як типами так і інтерфейсами, але однакової структури.

Якщо структура відрізняється - отримаєте помилку.

extends

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

interface A extends B {
  field: string
}

// --

interface A extends B, C {};

Також можна наслідуватись від об’єктних типів, але не від Mapped Types

Type A = { field: string };
Type B = { count: number };

interface C extends A, B {}

const entity: C = {} // { field: string; count: number; }

| (Union)

Оператор - union, використовується для декларації можливих варіантів типів.

type A = B | C;

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

-? та +?

Поля об’єкту можуть бути обов’язковими або опціональними, для таких полів можна використати наступні типи.

type Entity = { name: string };

type OptionalEntity = Partial<Entity>; // всі поля стали не обов'язковими

type RequiredEntity = Required<OptionalEntity>; // всі поля стали обов'язковими

Якщо говорити про реалізацію то ось як це можна зробити самостійно.

type CustomPartial<D> = {
 [k in keyof D]+?: D[k];
}

// або просто

type CustomPartial<D> = {
 [k in keyof D]?: D[k];
}

і навпаки

type CustomRequired<D> = {
 [k in keyof D]-?: D[k];
}

Інколи потрібно створити вкладені типи, у таких випадках це буде корисним.

Підсумки

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

Для того щоб називати себе професіоналом певної галузі, вам варто розуміти всі її аспекти, навіть якщо це щось не явне або абстрактне.

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

UPD: не було натхнення писати про “implements" тож допишу як з’явиться 🙃

Статті про вітчизняний бізнес та цікавих людей:

  • Вітаємо з Різдвом Христовим!

    Друкарня та платформа WE.UA вітають всіх наших читачів та авторів зі світлим святом Різдва! Зичимо всім українцям довгожданого миру, міцного здоровʼя, злагоди, родинного затишку та втілення всього доброго і прекрасного, чого вам побажали колядники!

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

    Різдво
  • Каблучки – прикраси, які варто купувати

    Ювелірні вироби – це не тільки спосіб витратити гроші, але і зробити вигідні інвестиції. Бо вартість ювелірних виробів з кожним роком тільки зростає. Тому купуючи стильні прикраси, ви вигідно вкладаєте кошти.

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

    Як Вибрати Каблучку
  • П'ять помилок у виборі домашнього текстилю, які псують комфорт сну

    Навіть ідеальний матрац не компенсує дискомфорт, якщо текстиль підібрано неправильно. Постільна білизна безпосередньо впливає на терморегуляцію, стан шкіри та глибину сну. Більшість проблем виникає не через низьку якість виробів, а через вибір матеріалів та подальшу експлуатацію

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

    Домашній Текстиль
  • Як знайти житло в Києві

    Переїжджаєте до Києва і шукаєте житло? Дізнайтеся, як орендувати чи купити квартиру, перевірити власника та знайти варіанти, про які зазвичай не говорять.

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

    Агентство Нерухомості
  • Як заохотити дитину до читання?

    Як залучити до читання сучасну молодь - поради та факти. Користь читання для дітей - основні переваги. Розвиток дітей - це наше майбутнє.

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

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

Software Engineer

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

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

  • Articles

    Зміст

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

    Content
  • Neural Network [guide] 1

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

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

    It
  • Frontend [TypeScript] 1

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

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

    It

Це також може зацікавити:

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

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

Це також може зацікавити: