Перейти к содержимому

Function Types: overloads, this, rest

Часто валят на нюансах: чем void отличается от undefined в return-позиции, что значит this-параметр, как работают overloads и почему лучше union вместо них. Красный флаг — писать overloads, когда хватает union, или возвращать boolean там, где нужен x is T.

void в TS — особый: callback, возвращающий что угодно, совместим с () => void.

const xs: number[] = [];
function each<T>(arr: T[], fn: (x: T) => void) { arr.forEach(fn); }
// Этот callback возвращает number, но контракт принимает любую возвращаемую функцию
each([1, 2], x => xs.push(x)); // ок: push возвращает number, а контракт void
// Зато явный () => undefined — это уже именно undefined
const f1: () => void = () => 42; // ок
// const f2: () => undefined = () => 42; // ошибка

Overloads — устаревшая практика для большинства случаев; обычно лучше union/conditional types.

// Старый стиль — overloads
function get(key: 'name'): string;
function get(key: 'age'): number;
function get(key: string): unknown {
return ({ name: 'Ada', age: 36 } as Record<string, unknown>)[key];
}
// Современнее — обобщённая функция через keyof
type User = { name: string; age: number };
const user: User = { name: 'Ada', age: 36 };
function pick<K extends keyof User>(k: K): User[K] { return user[k]; }

this-параметр — фейковый первый параметр для типизации контекста:

interface Btn { label: string }
function bindClick(this: Btn, e: { target: unknown }) {
console.log(this.label, e.target);
}
// bindClick.call({ label: 'Ok' }, { target: null });

Итог: Используй union/keyof вместо overloads, помни о специальном поведении void в callback-позиции, типизируй this, когда оно используется.