Типизация функций: параметры и возврат

Типизация функций в TypeScript — явное указание типов параметров и возвращаемого значения, что позволяет компилятору проверять корректность вызовов и гарантировать контракт функции.

Зачем нужно

Параметры функций — единственное место, где TypeScript не может вывести типы из контекста: без аннотаций они получат тип any (или ошибку при noImplicitAny). Явный возвращаемый тип документирует контракт и защищает от случайного изменения при рефакторинге.

Где используется

  • Все функции, принимающие данные снаружи (API, пользовательский ввод)
  • Публичное API модуля или класса
  • Callback-параметры и функции высшего порядка
  • Async-функции с явным типом промиса

Основной контент

Основной синтаксис

// Обычная функция
function add(a: number, b: number): number {
  return a + b;
}

// Стрелочная функция
const multiply = (a: number, b: number): number => a * b;

// Возвращаемый тип можно опустить — TypeScript выведет
const double = (n: number) => n * 2; // (n: number) => number

Опциональные и default параметры

function greet(name: string, title?: string): string {
  return title ? `${title} ${name}` : name;
}

function log(message: string, level: "info" | "warn" | "error" = "info"): void {
  console[level](message);
}

Rest параметры

function sum(...numbers: number): number {
  return numbers.reduce((acc, n) => acc + n, 0);
}

function join(separator: string, ...parts: string): string {
  return parts.join(separator);
}

sum(1, 2, 3, 4);          // 10
join("-", "a", "b", "c"); // "a-b-c"

Void и never

// void — функция ничего не возвращает
function log(msg: string): void {
  console.log(msg);
}

// never — функция никогда не завершится нормально
function fail(message: string): never {
  throw new Error(message);
}

function infiniteLoop: never {
  while (true) {}
}

Перегрузки (overloads)

// Объявления перегрузок
function format(value: string): string;
function format(value: number, decimals?: number): string;
// Реализация (должна покрывать все варианты)
function format(value: string | number, decimals = 2): string {
  if (typeof value === "string") return value.toUpperCase();
  return value.toFixed(decimals);
}

format("hello");    // string — первая перегрузка
format(3.14159, 2); // string — вторая перегрузка

Функциональные типы

// type alias для типа функции
type Predicate<T> = (value: T) => boolean;
type Transform<A, B> = (input: A) => B;
type Comparator<T> = (a: T, b: T) => number;

// Параметр-функция
function filter<T>(arr: T, predicate: Predicate<T>): T {
  return arr.filter(predicate);
}

const isPositive: Predicate<number> = (n) => n > 0;
filter([1, -2, 3, -4], isPositive); // [1, 3]

Async функции

// Явный тип возврата для async — Promise<T>
async function fetchUser(id: string): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

// Promise<void> — async без возврата значения
async function sendEmail(to: string, body: string): Promise<void> {
  await mailer.send({ to, body });
}

// Promise<User | null>
async function findUser(id: string): Promise<User | null> {
  const user = await db.users.findById(id);
  return user ?? null;
}

this parameter

interface Timer {
  seconds: number;
  start(this: Timer): void;
}

const timer: Timer = {
  seconds: 0,
  start(this: Timer) {
    setInterval(() => {
      this.seconds++; // this: Timer
    }, 1000);
  },
};

Частые ошибки

  • Не аннотировать параметры — с noImplicitAny: true это ошибка; без него — скрытый any.
  • Возвращать разные типы без явного unionreturn x > 0 ? "ok" : 42 выведет string | number; лучше явно указать.
  • Путать void и undefined — функция с void может вернуть undefined, но void и undefined не взаимозаменяемы как типы.
  • Перегрузки без реализации, покрывающей все случаи — реализация должна принимать union всех вариантов параметров.

Связанные темы

Ресурсы