Другое

TypeScript: стрелочные функции с обобщениями – Руководство

Изучите полный синтаксис стрелочных функций TypeScript с обобщениями. Узнайте, как писать обобщенные функции и применять лучшие практики.

Какой синтаксис используется для стрелочных функций TypeScript с обобщениями?
В справочнике TypeScript пока нет информации о стрелочных функциях с обобщениями. Обычные функции можно типизировать с помощью обобщений так:

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

Какой эквивалентный синтаксис для стрелочных функций?

Типобезопасные стрелочные функции в TypeScript используют тот же синтаксис параметров типа, что и обычные функции, но в форме стрелочной функции. Эквивалент стрелочной функции для вашего примера выглядит так:

typescript
const identity = <T>(arg: T): T => arg;

Содержание


Основной синтаксис стрелочной функции с обобщениями

Базовый синтаксис стрелочных функций с обобщениями в TypeScript выглядит так:

typescript
const functionName = <T>(parameter: T): T => {
    // тело функции
    return parameter;
};

Ключевые отличия от обычных функций:

  • ключевое слово const вместо function
  • стрелка => вместо фигурных скобок тела функции
  • параметр типа <T> размещается перед списком параметров
  • аннотация типа возвращаемого значения следует после списка параметров

Прямое сравнение:

Обычная функция:

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

Эквивалентная стрелочная функция:

typescript
const identity = <T>(arg: T): T => arg;

Обе функции работают одинаково, но используют разный синтаксис. Стрелочная версия более лаконична и часто используется в современных кодовых базах TypeScript.

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

Стрелочные функции могут обрабатывать несколько параметров типа так же, как и обычные функции:

typescript
// Обычная функция с несколькими обобщениями
function createPair<T, U>(first: T, second: U): [T, U] {
    return [first, second];
}

// Эквивалентная стрелочная функция
const createPair = <T, U>(first: T, second: U): [T, U] => [first, second];

Можно также использовать более сложные ограничения обобщений:

typescript
const getProperty = <T, K extends keyof T>(obj: T, key: K): T[K] => {
    return obj[key];
};

Вариации синтаксиса стрелочных функций

Стрелочные функции предлагают несколько синтаксических вариантов, которые работают с обобщениями:

Стрелочные функции с одной строкой

Для простых операций можно использовать лаконичный синтаксис одной строки:

typescript
const identity = <T>(arg: T): T => arg;
const double = <T extends number>(value: T): T => value * 2;

Многострочные стрелочные функции

Для более сложной логики используйте блоковый синтаксис:

typescript
const processArray = <T>(items: T[]): T[] => {
    const result = [...items];
    // Дополнительная логика обработки
    result.reverse();
    return result;
};

Стрелочные функции с необязательными параметрами

Стрелочные функции без проблем работают с необязательными параметрами:

typescript
const greet = <T extends string>(name: T, greeting?: string): string => {
    return greeting ? `${greeting}, ${name}!` : `Hello, ${name}!`;
};

Стрелочные функции с rest-параметрами

typescript
const concatAll = <T>(...items: T[]): T[] => {
    return items.reduce((acc, item) => [...acc, item], []);
};

Практические примеры и варианты использования

Функции высшего порядка с обобщениями

Стрелочные функции особенно полезны для функций высшего порядка:

typescript
const pipe = <T>(...fns: Array<(arg: T) => T>) => (value: T): T => {
    return fns.reduce((acc, fn) => fn(acc), value);
};

// Использование
const addOne = <T extends number>(x: T): T => (x + 1) as T;
const multiplyByTwo = <T extends number>(x: T): T => (x * 2) as T;

const addOneAndDouble = pipe(addOne, multiplyByTwo);
console.log(addOneAndDouble(5)); // Вывод: 12

Стрелочные функции с выводом типов

Вывод типов TypeScript работает отлично с обобщёнными стрелочными функциями:

typescript
const mapArray = <T, U>(array: T[], mapper: (item: T) => U): U[] => {
    return array.map(mapper);
};

// Вывод типов работает здесь
const numbers = [1, 2, 3, 4, 5];
const doubled = mapArray(numbers, x => x * 2); // Тип: number[]

Стрелочные функции в React-компонентах

В React обобщённые стрелочные функции часто используются для пользовательских хуков:

typescript
const useLocalStorage = <T>(key: string, initialValue: T) => {
    const [storedValue, setStoredValue] = useState<T>(() => {
        try {
            const item = window.localStorage.getItem(key);
            return item ? JSON.parse(item) : initialValue;
        } catch (error) {
            return initialValue;
        }
    });

    const setValue = (value: T | ((val: T) => T)) => {
        try {
            const valueToStore = value instanceof Function ? value(storedValue) : value;
            setStoredValue(valueToStore);
            window.localStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
            console.error(error);
        }
    };

    return [storedValue, setValue] as const;
};

Лучшие практики и соображения

Когда использовать стрелочные функции с обобщениями

  1. Предпочтение стрелочным функциям для:

    • Простых операций одной строкой
    • Функций высшего порядка
    • Когда нужна лексическая привязка this
    • При работе с функциональными паттернами
  2. Рассмотрите обычные функции для:

    • Сложных функций, которым нужна привязка this
    • Когда нужна подъем функции (hoisting)
    • При написании методов класса

Конвенции именования параметров типа

Используйте описательные имена параметров типа, когда они представляют конкретные типы:

typescript
// Хорошо
const getProperty = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];

// Более описательно
const getPropertyValue = <ObjectType, KeyType extends keyof ObjectType>(
    object: ObjectType,
    key: KeyType
): ObjectType[KeyType] => object[key];

Обработка сложных ограничений обобщений

Стрелочные функции отлично работают с сложными ограничениями:

typescript
const sortBy = <T>(array: T[], key: keyof T): T[] => {
    return [...array].sort((a, b) => {
        if (a[key] < b[key]) return -1;
        if (a[key] > b[key]) return 1;
        return 0;
    });
};

Производительность

Стрелочные функции с обобщениями имеют те же характеристики производительности, что и обычные функции. Проверка типов происходит во время компиляции, поэтому производительность во время выполнения идентична.

Обработка ошибок в обобщённых стрелочных функциях

typescript
const safeParse = <T>(json: string, defaultValue: T): T => {
    try {
        return JSON.parse(json);
    } catch {
        return defaultValue;
    }
};

Источники

  1. TypeScript Introduction - Type Parameters and Generic Functions
  2. Do Parentheses Matter in Arrow Functions? A TypeScript Deep Dive
  3. Arrow Function Syntax Guidelines

Заключение

Стрелочные функции с обобщениями в TypeScript следуют простому шаблону синтаксиса, который сохраняет согласованность с обычными обобщёнными функциями, при этом предлагая преимущества синтаксиса стрелочных функций. Ключевые выводы:

  • Используйте <T> перед списком параметров для параметров типа
  • Стрелочные функции предоставляют более лаконичный синтаксис для обобщённых операций
  • Они беспрепятственно работают с системой вывода типов TypeScript
  • Доступны как однострочные, так и многострочные варианты синтаксиса
  • Стрелочные функции особенно подходят для функций высшего порядка и функциональных паттернов

Синтаксис const identity = <T>(arg: T): T => arg; обеспечивает ту же типобезопасность и функциональность, что и обычная функция, но при этом улучшает читаемость и лаконичность во многих сценариях.

Авторы
Проверено модерацией
Модерация