TypeScript — это мощный инструмент для современной JavaScript-разработки, обеспечивающий типобезопасность и предоставляющий расширенные возможности.
Многие разработчики знают основы TypeScript, но знание некоторых приемов может сделать ваш код более эффективным, чистым и поддерживаемым. Давайте рассмотрим эти премы!
1. NonNullable
TypeScript предоставляет утилиту NonNullable для исключения null и undefined из типа. Это поможет вам избежать неожиданных нулевых значений.
type User = { name: string; age?: number | null };
const user: NonNullable<User["age"]> = 30; //
No null or undefined allowed
2. Использование Partial для гибкости
Утилита Partial<T> делает все свойства типа необязательными. Это очень удобно, когда вы обновляете только подмножество полей объекта.
interface User {
name: string;
age: number;
email: string;
}
const updateUser = (user: Partial<User>) => {
// 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<T> сделает все свойства типа неизменяемыми, предотвращая переприсваивание.
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<T> = {
[K in Status]: T;
};
const response: ApiResponse<string> = {
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<Todo, 'description'>;
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> = 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<T, 'a' | 'b'>; // 'a' | 'b' type ExcludeC = Exclude<T, 'c'>; // '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<K, V> — идеальный вариант.
type Role = 'admin' | 'user' | 'guest';
const permissions: Record<Role, string[]> = {
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<T> извлекает тип возвращаемого значения функции. Это может быть полезно, когда вы имеете дело со сложными типами.
function getUser() {
return { name: 'John', age: 30 };
}
type UserReturn = ReturnType<typeof getUser>; // { name: string; age: number; }
19. Параметры типов в функциях
Параметры дженерик-типов делают ваши функции гибкими и пригодными для повторного использования в различных типах.
function identity<T>(value: T): T {
return value;
}
identity<string>('Hello'); // 'Hello'
identity<number>(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.
СМОТРИТЕ ТАКЖЕ:


