Условные типы TypeScript

Условные типы в TypeScript предоставляют способ создания типов, зависящих от условия. Они обеспечивают большую гибкость и выразительность в определениях типов, позволяя моделировать сложные отношения типов в ясной и лаконичной манере. В этой статье рассматривается, как условные типы работают в TypeScript, и приводятся примеры, иллюстрирующие их использование.

Что такое условные типы?

Условные типы позволяют создавать типы, которые выбираются на основе условия. Они похожи на условные операторы в программировании, но работают на уровне типа. Базовый синтаксис условного типа:

type ConditionalType = T extends U ? X : Y;

В этом синтаксисе:

  • T — проверяемый тип.
  • Тип для сравнения — U.
  • X — тип, возвращаемый, если T расширяет U.
  • Y — тип, возвращаемый, если T не расширяет U.

Базовый пример условных типов

Вот простой пример условного типа, который возвращает различные типы в зависимости от того, является ли данный тип строкой или нет:

type IsString = T extends string ? "String" : "Not a string";

type Result1 = IsString;  // Result1 is "String"
type Result2 = IsString;  // Result2 is "Not a string"

В этом примере IsString проверяет, расширяет ли Tstring. Если да, то результатом будет "String"; в противном случае результатом будет "Not a string".

Использование условных типов с универсальными типами

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

type ReturnType = T extends (...args: any[]) => infer R ? R : never;

type FunctionType = (x: number) => string;

type Result = ReturnType;  // Result is string

В этом примере ReturnType использует ключевое слово infer для вывода типа возвращаемого значения R типа функции T. Если T является типом функции, ReturnType будет типом возвращаемого значения; в противном случае по умолчанию используется never.

Условные типы с типами объединения

Условные типы также могут работать с типами union для обработки нескольких возможных типов. Например, различая разных членов union:

type ExtractString = T extends string ? T : never;

type UnionType = string | number | boolean;

type Result = ExtractString;  // Result is string

В этом примере ExtractString извлекает string из типа объединения UnionType, в результате чего получается string.

Условные типы с сопоставлением типов

Условные типы можно комбинировать с отображениями типов для создания более сложных преобразований типов. Например, отображение массива типов для применения условного типа:

type MapArray = {
  [K in keyof T]: T[K] extends string ? T[K] : never;
};

type ArrayType = [string, number, boolean];

type MappedArray = MapArray;  // MappedArray is [string, never, never]

В этом примере MapArray сопоставляет каждый элемент массива T и применяет условный тип к каждому элементу, в результате чего получается массив, в котором сохраняются только строковые элементы.

Заключение

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