3 паттерна TypeScript, которые я использую в своей повседневной работе

RedDeveloper
21.02.2023 13:22
3 паттерна TypeScript, которые я использую в своей повседневной работе

в качестве фрилансера на Toptal

1. Свойства только для чтения

В TypeScript 2.0 в язык был добавлен модификатор readonly.

Свойства, помеченные readonly, могут быть присвоены только во время инициализации или из конструктора того же класса. Все остальные назначения запрещены.

  • Пример
// type definitions

interface Person { readonly name: string; readonly age: number; };

// or

type Person = { readonly name: string; readonly age: number; };

// Usage:

const person: Person = { name: Mike, age: 23 };

Теперь, поскольку свойства name и age объявлены как readonly, значение любого из них не может быть переназначено во время компиляции:

person.name="Kate" // not allowed
person.age = 33 // not allowed

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

Также стоит прямо заявить, что модификатор readonly является частью системы типов TypeScript, и что после компиляции кода Typescript в JavaScript модификаторы readonly удаляются из кода. Поэтому не существует защиты от переназначения свойств во время выполнения.

2. Защита типов

Защита типов - это техника, используемая для получения типа переменной внутри условного блока.

Защитники типов обычно реализуются в виде функций.

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

TypeScript использует некоторые встроенные операторы JavaScript, такие как typeof, instanceof и оператор in, которые используются для определения того, содержит ли объект свойство.

  • Пример 1 - использование оператора "typeof" для построения защиты типа
const isString = (value: unknown): value is string => typeof value === "string";

// ....

if (isString(value)) {
  console.info(value.toUpperCase());
}

Оператор typeof может определять следующие типы, распознаваемые JavaScript; boolean, string, bigint, symbol, undefined, function, number.

  • Пример 2 - использование оператора "in" для построения защиты типа
interface Vehicle {
  manufacturer: VehicleManufacturer;
  model: VehicleModel;
}

interface Animal {
  name: string;
  species: AnimalSpecies;
}

const getUniqueID = (entity: Vehicle | Animal) => {
  if ("manufacturer" in entity) {
    return entity.manufacturer; 
  }

  if ("species" in entity) {
    return `${entity.name} - ${entity.species}`;
  }

  ...
} 

Оператор in проверяет, обладает ли объект определенным свойством. Он также обычно реализуется как функция и возвращает булеву величину.

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

3. Союзные типы

Тип объединения описывает значение, которое может быть одним из нескольких типов. Мы используем вертикальную полосу (|) для разделения каждого типа, поэтому число | строка | булево - это тип значения, которое может быть числом, строкой или булевым числом.

  • Пример 1 - объединение примитивов
type Status="active" | "inactive" | "pending";

const setStatus = (status: Status) => {
  // ...
}

setStatus("active");
setStatus("pending");
setStatus("invalid"); // Error: Argument of type '"invalid"' is not assignable to parameter of type 'Status'.
  • Пример 2 - сужение союза

Welcome to DeepL Write!

// type guards

const isString = (value: unknown): value is string => {
  return typeof value === "string";
}

const isNumber = (value: unknown): value is number => {
  return typeof value === "number";
}

// simple union type

type ID="string" | "number";

const getNameFromID = (id: ID) => {
  if (isString(id)) {
    // logic that deals with id: string
  }

  if (isNumber(id)) {
  // logic that deals with id: number
  }

  // default return, or error handling logic
}

getNameFromID("23");

getNameFromID(45);
    This tool allows you to correct mistakes, rephrase sentences and improve your writing. The green highlight on the right indicates a change.
interface Bird {
  weight: number;
  canFly: boolean;
}

interface Dog {
  age: number;
  canFly: boolean;
}

const canAnimalFly = (animal: Bird | Dog) => animal.canFly;

// even better for readability and scalability purposes 

type Animal = Bird | Dog;

const canAnimalFly = (animal: Animal) => animal.canFly;

Click on a word to see suggestions or rewrite the whole sentence.

Но что, если мы хотим получить доступ к свойству weight для объекта bird: Объект "Птица", или свойство "Возраст" у собаки: Объект Dog?

type Animal = Bird | Dog;

const bird: Animal = bird.age // TS error

Компилятор TypeScript пожаловался бы и выдал ошибку.

Поскольку TS является структурно типизированным языком, он разрешает доступ только к свойствам, определенным в обоих интерфейсах.

Техника защиты типов, которая помогает нам удовлетворить компилятор TS:

interface Bird {
  weight: number;
  canFly: boolean;
}

interface Dog {
  age: number;
  canFly: boolean;
}

type Animal = Bird | Dog;

const getAnimalAge = (animal: Animal) => {
  if ("age" in animal) {
    return animal.age;
  }

  return undefined;
}

const getAnimalWeight = (animal: Animal) => {
  if ("weight" in animal) {
    return animal.weight;
  }

  return undefined;
}

Теперь представьте, что мы добавили больше общих свойств в интерфейс Bird или Dog. Или что если мы удалим некоторые общие свойства из обоих интерфейсов, которые также используются внутри защит типов? Например, canFly.Итак, вот модифицированный пример вышеприведенного кода, использующий метод

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

Например:

interface Bird {
  weight: number;
  canFly: boolean;
  ANIMAL_TYPE: "BIRD";
}

interface Dog {
  age: number;
  canFly: boolean;
  ANIMAL_TYPE: "DOG";
}

type Animal = Bird | Dog;

const getAnimalAge = (animal: Animal) => {
  if (animal.ANIMAL_TYPE === "DOG") {
    return animal.age;
  }

  return undefined;
}

const getAnimalWeight = (animal: Animal) => {
  if (animal.ANIMAL_TYPE === "BIRD") {
    return animal.weight;
  }

  return undefined;
}

свойство нашим JS-объектам (которые не имеют никакой бизнес-ценности). А это увеличивает потребление памяти - но, вероятно, не намного.

Заключение

Спасибо, что дочитали эту статью до конца.

Надеюсь, сегодня вы узнали что-то новое. А если нет, то, возможно, это помогло вам укрепить некоторые концепции.Недостатком этого подхода является то, что мы присоединяем дополнительное свойство к нашему JS-объекту.

.org/docs/handbook/typescript-in-5-minutes.html

2) https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html В будущем я буду публиковать больше подобных материалов, так что следите за новостями :)

Ресурсы:

1) https://www.typescriptlang

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?

20.08.2023 18:21

Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в 2023-2024 годах? Или это полная лажа?".

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией

20.08.2023 17:46

В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.

Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox

19.08.2023 18:39

Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в частности, магию поплавков и гибкость flexbox.

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest

19.08.2023 17:22

В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для чтения благодаря своей простоте. Кроме того, мы всегда хотим проверить самые последние возможности в наших проектах!

Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️

18.08.2023 20:33

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL

14.08.2023 14:49

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