Как типизировать функции в TypeScript?

MiddleTypeScript #typescript #functions

Вопрос

Как типизировать параметры и возвращаемое значение функции, задавать опциональные/значения по умолчанию и что такое перегрузки функций?

Короткий ответ

Типы параметров указываются после имени через двоеточие, тип возврата — после списка параметров. Опциональные параметры помечаются ?, параметры со значением по умолчанию сами выводят тип. Перегрузки (function overloads) позволяют описать несколько сигнатур для одной функции с разным поведением в зависимости от типов аргументов.

Подробный ответ

Базовая типизация функции:

function sum(a: number, b: number): number {
  return a + b
}

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

Опциональные параметры (?) могут быть не переданы — тогда их значение undefined. Они обязаны идти после обязательных:

function createUser(name: string, age?: number) {
  // age: number | undefined
}

Параметры по умолчанию делают параметр опциональным при вызове, а TypeScript выводит его тип из значения по умолчанию, если тип не указан явно:

function createUser(name: string, role = 'user') {
  // role: string, при вызове необязателен
}

Перегрузки функций нужны, когда поведение и тип возврата функции зависят от формы входных аргументов так, что union-типа недостаточно для точного описания связи вход → выход. Пишется несколько сигнатур подряд, а следом — одна реализация, которая должна быть совместима со всеми сигнатурами (сама реализация не видна вызывающему коду):

function makeDate(timestamp: number): Date
function makeDate(day: number, month: number, year: number): Date
function makeDate(a: number, b?: number, c?: number): Date {
  if (b !== undefined && c !== undefined) return new Date(c, b - 1, a)
  return new Date(a)
}

makeDate(2026)               // OK — первая сигнатура
makeDate(7, 9, 2026)         // OK — вторая сигнатура
makeDate(7, 9)                // Ошибка — нет сигнатуры с двумя аргументами

Без перегрузок пришлось бы писать (a: number, b?: number, c?: number) => Date, и тогда вызов makeDate(7, 9) не был бы отклонён компилятором.

Пример

interface FetchOptions {
  method?: 'GET' | 'POST'
  retries?: number
}

// явный тип возврата в публичной функции
async function fetchData<T>(url: string, options: FetchOptions = {}): Promise<T> {
  const { method = 'GET', retries = 3 } = options
  const res = await fetch(url, { method })
  if (!res.ok) throw new Error(`Failed: ${res.status}`)
  return res.json() as Promise<T>
}

// перегрузки для парсера конфигурации
function parseConfig(input: string): Record<string, string>
function parseConfig(input: string[]): Record<string, string>[]
function parseConfig(input: string | string[]): Record<string, string> | Record<string, string>[] {
  const parseOne = (s: string) => JSON.parse(s) as Record<string, string>
  return Array.isArray(input) ? input.map(parseOne) : parseOne(input)
}

Дополнительные вопросы

  • Чем сигнатура функции с union-параметром отличается от перегрузок и когда выбрать одно вместо другого?
  • Как типизировать функцию, принимающую произвольное число аргументов (rest parameters)?
  • Что такое this параметр в типизации функций и зачем он нужен?