Функции в TypeScript
Дальше: Дженерики · Асинхронность · Объекты и классы · Справочник — функции
Учебное раскрытие Справочник — функции: сигнатуры, overloads, this, generic-функции.
В JavaScript функции — значения первого класса: их передают в аргументах, возвращают из функций, хранят в переменных. TypeScript добавляет контракт сигнатуры: какие аргументы допустимы, что возвращается, как типизировать callback и перегрузки.
Маршрут: Типы и типизация → функции → дженерики → асинхронность.
База по JS: функции и объекты. Общая теория — блоки кода.
Типизация функций и сигнатуры
| Без типов (JS) | С TypeScript |
|---|---|
| Неверный аргумент всплывает в runtime | Ошибка в IDE и при tsc |
| Callback с лишним полем не заметен | Сигнатура onSuccess(data: User) |
| Рефакторинг параметра ломает вызовы молча | Компилятор показывает все места |
Проверка идёт на этапе компиляции — в .js аннотации исчезают, runtime остаётся JavaScript.
Объявление функций
Обычная функция
function sum(a: number, b: number): number {
return a + b;
}
Разбор:
- После имени параметра — тип:
a: number. - После
)— тип возврата:: number. return "text"вызовет ошибку компиляции.
Стрелочная функция
const multiply = (x: number, y: number): number => x * y;
const greet = (name: string): string => {
return `Привет, ${name}`;
};
Разбор:
- С телом
{ }нужен явныйreturn(кромеvoid). - Выражение после
=>— неявныйreturn(как в JS).
Вывод типа возврата
Если тип возврата не указан, компилятор выводит его из return:
function double(n: number) {
return n * 2; // выведено: number
}
Для публичного API (экспорт модуля) лучше указать возврат явно — так контракт останется стабильным при правках тела функции.
void, undefined и отсутствие return
function log(message: string): void {
console.log(message);
// return; — допустимо
// return undefined; — допустимо
// return 42; — ошибка
}
| Тип | Смысл |
|---|---|
void | Вызывающему не важен результат |
undefined | Должны вернуть именно undefined |
без return | Для void — OK; для строгого undefined — нужен return undefined |
Async-функции возвращают Promise<T> — асинхронность.
Параметры: обязательные, опциональные, по умолчанию
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
function createId(prefix = "id"): string {
return `${prefix}-${Date.now()}`;
}
Разбор:
title?— аргумент можно не передавать; типstring | undefined.prefix = "id"— TypeScript выводит типstringиз значения по умолчанию.- Опциональные параметры должны идти после обязательных (как в JS).
Rest-параметры
function logAll(level: string, ...messages: string[]): void {
for (const m of messages) {
console.log(`[${level}]`, m);
}
}
logAll("info", "старт", "готово");
Тип rest — массив (string[]), не any[], если включён strict.
Деструктуризация параметров
type Point = { x: number; y: number };
function printPoint({ x, y }: Point): void {
console.log(x, y);
}
function connect({ host, port = 8080 }: { host: string; port?: number }): void {
console.log(`${host}:${port}`);
}
Разбор:
- Вся форма объекта — один аргумент с типом
Point. - Значения по умолчанию работают как в JS; тип
portвыводится какnumber.
Тип функции (сигнатура)
Функция — тоже тип. Его выносят в type или interface:
type BinaryOp = (x: number, y: number) => number;
const add: BinaryOp = (a, b) => a + b;
const sub: BinaryOp = (a, b) => a - b;
type UserHandler = (user: User) => void;
function loadUser(id: string, onSuccess: UserHandler, onError: (err: Error) => void) {
// ...
}
Разбор:
BinaryOpописывает любую функцию с двумяnumberи результатомnumber.- Имена параметров в типе (
x,y) не обязаны совпадать с реализацией (a,b).
Callable interface
interface Validator {
(value: string): boolean;
cache?: Map<string, boolean>;
}
const validateEmail: Validator = (value) => value.includes("@");
Подходит, когда функция — объект с дополнительными полями (кэш, метаданные).
Таблицы сигнатур — Справочник — функции и интерфейсы.
Callback и события
Типичный паттерн в UI и API:
type User = { id: string; name: string };
type FetchCallbacks = {
onStart?: () => void;
onSuccess: (user: User) => void;
onError: (error: unknown) => void;
};
async function fetchUser(id: string, callbacks: FetchCallbacks): Promise<void> {
callbacks.onStart?.();
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = (await res.json()) as User;
callbacks.onSuccess(user);
} catch (error: unknown) {
callbacks.onError(error);
}
}
Разбор:
onStart?— опциональный callback без аргументов.onErrorпринимаетunknown, неany— см. Типы и типизация и Обработка ошибок.?.()безопасно вызывает отсутствующий callback.
Стрелочные функции и this
В JavaScript this зависит от способа вызова. Стрелочные функции не имеют своего this — берут из внешней области.
class Counter {
count = 0;
// Обычный метод — свой this
increment() {
this.count += 1;
}
// Стрелка как поле — this зафиксирован на экземпляре
incrementBound = () => {
this.count += 1;
};
}
const c = new Counter();
const fn = c.increment;
// fn(); // в strict JS/TS — this может быть undefined
const bound = c.incrementBound;
bound(); // OK: count увеличится
Для DOM в TS типизируют this явно, если нужен обычный метод:
button.addEventListener("click", function (this: HTMLButtonElement, event: MouseEvent) {
this.disabled = true;
});
Подробнее про классы — 18.md; события — 20.md.
Перегрузки (overloads)
Несколько сигнатур для одной реализации — удобный API для вызывающего:
function format(value: string): string;
function format(value: number): string;
function format(value: string | number): string {
return String(value);
}
const a = format("42"); // string
const b = format(42); // string
Разбор:
- Строки
function format(...)без тела — объявления перегрузок. - Последняя сигнатура с телом должна принимать объединение всех вариантов.
- Вызывающий видит точный возврат по типу аргумента; реализация одна.
Перегрузка с разным числом аргументов
function createElement(tag: "img"): HTMLImageElement;
function createElement(tag: "div"): HTMLDivElement;
function createElement(tag: string): HTMLElement;
function createElement(tag: string): HTMLElement {
return document.createElement(tag);
}
Не злоупотребляйте перегрузками: часто проще один аргумент-объект или discriminated union — Типы и типизация.
Функции высшего порядка
Функция, принимающая или возвращающая другую функцию:
function filter<T>(items: T[], predicate: (item: T) => boolean): T[] {
const result: T[] = [];
for (const item of items) {
if (predicate(item)) result.push(item);
}
return result;
}
const evens = filter([1, 2, 3, 4], (n) => n % 2 === 0);
const long = filter(["a", "bb", "ccc"], (s) => s.length > 1);
Разбор:
<T>связывает тип элементов массива и тип вpredicate.- Без generic пришлось бы
any[]или дублировать код — см. дженерики.
Type-driven handler
Сначала тип события, затем обработчик:
type InputEvent = {
type: "input";
value: string;
};
type ClickEvent = {
type: "click";
x: number;
y: number;
};
type AppEvent = InputEvent | ClickEvent;
function handleEvent(event: AppEvent): void {
switch (event.type) {
case "input":
console.log(event.value);
return;
case "click":
console.log(event.x, event.y);
return;
}
}
Компилятор проверяет, что в каждой ветке доступны нужные поля — см. операторы.
Generic-функции
function identity<T>(value: T): T {
return value;
}
function first<T>(items: T[]): T | undefined {
return items[0];
}
Ограничения и keyof — в дженерики, контракты API — Паттерны.
Утилиты для сигнатур
Встроенные типы над типами функций:
type UserService = {
getUser(id: string): Promise<User>;
deleteUser(id: string): Promise<void>;
};
// Тип параметров функции
type DeleteParams = Parameters<UserService["deleteUser"]>; // [id: string]
// Тип возврата
type UserResult = Awaited<ReturnType<UserService["getUser"]>>; // User
Полный список — Справочник — утилитарные и расширенные типы.
noImplicitAny и параметры
С strict / noImplicitAny у каждого параметра должен быть известный тип (явно или через контекст):
// Ошибка без strict: (item) => ...
// С noImplicitAny: нужен тип item
items.forEach((item: string) => console.log(item));
// Или generic / тип массива задаёт item автоматически:
const names: string[] = ["a", "b"];
names.forEach((item) => console.log(item.toUpperCase()));
Частые ошибки
| Ошибка | Причина | Что делать |
|---|---|---|
Expected 2 arguments, but got 1 | Сигнатура не совпадает | Исправить вызов или сделать параметр опциональным |
Type '() => void' is not assignable... | Неверный callback | Сверить тип аргумента и Handler |
| Перегрузка не срабатывает | Тело не покрывает union | Реализация должна принимать все варианты |
this undefined в методе | Потерян контекст вызова | Стрелка, .bind, или поле-стрелка в классе |
Слишком широкий Function | Потеря типов | Конкретный (a: T) => R |
Async без Promise<T> | Забыли тип возврата | async function f(): Promise<User> |
Практика
- Опишите
type OnSave = (data: FormData) => voidи функциюsubmit(form: FormData, hooks: { onSave: OnSave }). - Напишите перегрузку
parse(s: string): numberиparse(n: number): stringс одной реализацией. - Реализуйте
map<T, U>(items: T[], fn: (item: T) => U): U[]и используйте с разными типами. - В классе замените метод, передаваемый в
setTimeout, на стрелочное поле — убедитесь, чтоthisкорректен. - Добавьте
handleEventс discriminated union из трёх вариантов и exhaustivedefaultсnever.
Смежные статьи
- Типы данных и типизация — union, narrowing,
unknown - Переменные и константы —
constдля функций - Дженерики —
<T>,extends,keyof - Асинхронность —
async/await,Promise<T> - Объекты и классы — методы, модификаторы
- Обработка ошибок —
catch (unknown) - Справочник раздела · Справочник — функции
- Обзор: Обзор TypeScript в JS