20 приемов TypeScript, которые должен знать каждый разработчик

Многие разработчики знают основы TypeScript, но знание некоторых приемов может сделать ваш код более эффективным, чистым и поддерживаемым. Запись 20 приемов TypeScript, которые должен знать каждый разработчик впервые появилась techrocks.ru.

Фев 20, 2025 - 19:55
 0
20 приемов TypeScript, которые должен знать каждый разработчик

TypeScript — это мощный инструмент для современной JavaScript-разработки, обеспечивающий типобезопасность и предоставляющий расширенные возможности.

Многие разработчики знают основы TypeScript, но знание некоторых приемов может сделать ваш код более эффективным, чистым и поддерживаемым. Давайте рассмотрим эти премы!

1. NonNullable

TypeScript предоставляет утилиту NonNullable для исключения null и undefined из типа. Это поможет вам избежать неожиданных нулевых значений.

type User = { name: string; age?: number | null };
const user: NonNullable = 30; // ✅ No null or undefined allowed

2. Использование Partial для гибкости

Утилита Partial делает все свойства типа необязательными. Это очень удобно, когда вы обновляете только подмножество полей объекта.

interface User {
  name: string;
  age: number;
  email: string;
}

const updateUser = (user: Partial) => {
  // You can pass only the fields you want to update
  return { ...user, updatedAt: new Date() };
};

updateUser({ name: 'John' }); // No need to provide the entire object

3. Использование Readonly для неизменяемых данных

Если вам нужна неизменяемость в TypeScript, Readonly сделает все свойства типа неизменяемыми, предотвращая переприсваивание.

const config: Readonly<{ apiUrl: string; retries: number }> = {
  apiUrl: 'https://api.example.com',
  retries: 5
};

config.apiUrl = 'https://newapi.com'; // ❌ Error: Cannot assign to 'apiUrl' because it is a read-only property

4. Сопоставленные типы для динамической типизации свойств

Сопоставленные типы позволяют создавать новые типы путем преобразования существующих. Это удобно для создания вариаций типа объекта.

type Status = 'loading' | 'success' | 'error';
type ApiResponse = {
  [K in Status]: T;
};

const response: ApiResponse = {
  loading: 'Fetching...',
  success: 'Data loaded',
  error: 'Something went wrong'
};

5. Типы кортежей с опциональными элементами

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

type UserTuple = [string, number?, boolean?];

const user1: UserTuple = ['Alice'];          // ✅ Just the name
const user2: UserTuple = ['Bob', 30];        // ✅ Name and age
const user3: UserTuple = ['Charlie', 25, true]; // ✅ Full tuple

6. Union-типы с исчерпывающими проверками

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

type Status = 'open' | 'closed' | 'pending';

function handleStatus(status: Status) {
  switch (status) {
    case 'open':
      return 'Opened';
    case 'closed':
      return 'Closed';
    case 'pending':
      return 'Pending';
    default:
      const exhaustiveCheck: never = status; // ❌ Error if a new status type is added but not handled
      return exhaustiveCheck;
  }
}

7. Тип Omit для исключения ключей

Иногда вам нужно создать тип объекта, исключающий определенные ключи. В этом случае Omit — ваш друг!

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Omit;

const todo: TodoPreview = {
  title: 'Learn TypeScript',
  completed: false
};

8. Сужение типов с помощью in и instanceof

Используйте in и instanceof для сужения типов во время выполнения программы.

function processInput(input: string | number | { title: string }) {
  if (typeof input === 'string') {
    return input.toUpperCase(); // Narrowed to string
  } else if (typeof input === 'number') {
    return input * 2; // Narrowed to number
  } else if ('title' in input) {
    return input.title; // Narrowed to object with title property
  }
}

9. Условные типы для расширенной логики типов

Условные типы обеспечивают невероятную гибкость при преобразовании типов на основе условий.

type IsString = T extends string ? true : false;

type CheckString = IsString<'Hello'>; // true
type CheckNumber = IsString<42>; // false

10. Использование as const для неизменяемых литеральных типов

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

const COLORS = ['red', 'green', 'blue'] as const;

type Color = typeof COLORS[number]; // 'red' | 'green' | 'blue'

11. Extract и Exclude для уточнения типов

Используйте Extract и Exclude, чтобы отфильтровать или выбрать определенные типы из объединения.

type T = 'a' | 'b' | 'c';
type OnlyAOrB = Extract; // 'a' | 'b'
type ExcludeC = Exclude; // 'a' | 'b'

12. Защитники типа для пользовательской проверки

Создавайте собственных защитников типов (type guards) для динамического уточнения типов во время выполнения.

function isString(input: any): input is string {
  return typeof input === 'string';
}

const value: any = 'Hello';

if (isString(value)) {
  console.log(value.toUpperCase()); // Safe: value is a string here
}

13. Использование Record для типов динамических объектов

Когда вам нужен тип для объекта с динамическими ключами, Record — идеальный вариант.

type Role = 'admin' | 'user' | 'guest';
const permissions: Record = {
  admin: ['read', 'write', 'delete'],
  user: ['read', 'write'],
  guest: ['read']
};

14. Динамические свойства классов с индексными сигнатурами

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

class DynamicObject {
  [key: string]: any;
}

const obj = new DynamicObject();
obj.name = 'Alice';
obj.age = 30;

15. Тип never для невозможных состояний

Тип never представляет значения, которые никогда не должны встречаться. Он часто используется в исчерпывающих проверках.

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

16. Опциональные цепочки для безопасного доступа к свойствам

Используйте оператор опциональной последовательности (?.) для безопасного доступа к глубоко вложенным свойствам. Это позволит не опасаться ошибок, связанных с undefined.

const user = { profile: { name: 'John' } };
const userName = user?.profile?.name; // 'John'
const age = user?.profile?.age ?? 'Not provided'; // Fallback to default

17. Установка значений по умолчанию с помощью оператора нулевого слияния (??)

Используйте оператор нулевого слияния, чтобы предоставить запасное значение в том случае, если исходное значение равно null или undefined.

const input: string | null = null;
const defaultValue = input ?? 'Default'; // 'Default'

18. Вывод типов возвращаемых значений с помощью ReturnType

Утилита ReturnType извлекает тип возвращаемого значения функции. Это может быть полезно, когда вы имеете дело со сложными типами.

function getUser() {
  return { name: 'John', age: 30 };
}

type UserReturn = ReturnType; // { name: string; age: number; }

19. Параметры типов в функциях

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

function identity(value: T): T {
  return value;
}

identity('Hello'); // 'Hello'
identity(42); // 42

20. Типы пересечения для объединения структур

Типы пересечения (intersection types) позволяют объединять несколько типов в один.

type Admin = { privileges: string[] };
type User = { name: string };

type AdminUser = Admin & User;

const adminUser: AdminUser = {
  privileges: ['admin', 'editor'],
  name: 'Alice'
};

Эти приемы помогут вам поднять ваши навыки работы с TypeScript на новый уровень! Продолжайте экспериментировать и внедрять эти паттерны в свои проекты, чтобы код был чище и эффективнее. Успешного кодинга!

Перевод статьи “20 TypeScript Tricks Every Developer Should Know”.

Запись 20 приемов TypeScript, которые должен знать каждый разработчик впервые появилась techrocks.ru.