Урок 02

Дженерик-функции

Дженерик-функции принимают параметр типа и работают с любым типом.

Базовый пример

function identity<T>(value: T): T {
    return value;
}

const a = identity('hello'); // string
const b = identity(42);      // number
const c = identity({ x: 1 }); // { x: number }

Несколько параметров

function pair<A, B>(a: A, b: B): [A, B] {
    return [a, b];
}

const p = pair('x', 1); // [string, number]

Дженерик-массивы

function first<T>(items: T[]): T | undefined {
    return items[0];
}

function last<T>(items: T[]): T | undefined {
    return items[items.length - 1];
}

function filter<T>(items: T[], predicate: (item: T) => boolean): T[] {
    return items.filter(predicate);
}

Map

function map<T, U>(items: T[], fn: (item: T) => U): U[] {
    return items.map(fn);
}

const lengths = map(['a', 'bb', 'ccc'], (s) => s.length);
// number[]

TypeScript выводит U из возвращаемого типа функции.

Функция с callback

function tap<T>(value: T, fn: (value: T) => void): T {
    fn(value);
    return value;
}

tap(5, (n) => console.log(n.toFixed(2)));

Тип n выведен как number.

Async-функции

async function fetchData<T>(url: string): Promise<T> {
    const res = await fetch(url);
    return res.json() as Promise<T>;
}

const user = await fetchData<User>('/api/user');

Явное указание типа нужно, потому что res.json() возвращает any.

Обёртка с сохранением типа

type Result<T> = { data: T; error: null } | { data: null; error: string };

function ok<T>(data: T): Result<T> {
    return { data, error: null };
}

function fail<T>(error: string): Result<T> {
    return { data: null, error };
}

Дефолтное значение

type ApiResponse<T = unknown> = {
    data: T;
    status: number;
};

const res: ApiResponse = { data: 'any', status: 200 };
// data: unknown

Возврат кортежа

function toArray<T>(value: T): [T] {
    return [value];
}

const arr = toArray(5); // [5]

Практика: useToggle

function useToggle(initial: boolean): [boolean, () => void] {
    const [value, setValue] = useState(initial);
    const toggle = () => setValue((v) => !v);
    return [value, toggle];
}

Здесь дженерик не нужен — тип фиксированный.

Дженерик + ограничение

function logLength<T extends { length: number }>(value: T): T {
    console.log(value.length);
    return value;
}

logLength('hello');
logLength([1, 2, 3]);
logLength(42); // ошибка

Итоги

  • <T> перед аргументами
  • TypeScript выводит тип
  • Несколько параметров через запятую
  • Работает с async и callback
  • Ограничения — extends