Функции
Типизация функций, параметры и возвращаемые значения, необязательные/значения по умолчанию/rest-параметры и перегрузки функций.
6 вопросов
JuniorТеорияОчень частоКак типизировать параметры и возвращаемое значение функции?
Как типизировать параметры и возвращаемое значение функции?
Каждый параметр аннотируется как (name: T), а возвращаемый тип пишется после скобок: function add(a: number, b: number): number. Возвращаемый тип также может быть выведен из тела. Параметры могут быть необязательными через ? (после обязательных), иметь значение по умолчанию через = или быть rest-параметром ...args: T[]. Самостоятельный тип функции — это (x: number) => string, а void отмечает функцию, ничего не возвращающую.
Типичные ошибки
- ✗Размещать необязательный параметр с
?перед обязательным — необязательные должны идти последними - ✗Писать rest-параметр как
args...: Tвместо...args: T[] - ✗Считать, что каждый возвращаемый тип нужно аннотировать, хотя он обычно выводится
Уточняющие вопросы
- →Когда стоит аннотировать возвращаемый тип, несмотря на вывод типов?
- →В чём разница между необязательным параметром и параметром по умолчанию?
JuniorТеорияЧастоЧем необязательный параметр ? отличается от параметра со значением по умолчанию?
Чем необязательный параметр ? отличается от параметра со значением по умолчанию?
Оба позволяют вызывающему опустить аргумент, но тип внутри тела разный. ? расширяет параметр до T | undefined, поэтому его надо проверить перед использованием. Значение по умолчанию через = v сохраняет тип T, ведь оно подставляется всякий раз, когда аргумент равен undefined. Совместить оба на одном параметре нельзя.
Типичные ошибки
- ✗Писать
f(x?: number = 0)—?и default нельзя совмещать на одном параметре - ✗Забывать, что
?типизирует параметр какT | undefined, и использовать его без проверки - ✗Считать, что default срабатывает на
null— он срабатывает только наundefined
Уточняющие вопросы
- →Почему необязательный параметр обязан идти после всех обязательных?
- →Что произойдёт, если вызывающий явно передаст
undefinedв параметр с default?
MiddleТеорияЧастоЧто такое перегрузка функций в TypeScript?
Что такое перегрузка функций в TypeScript?
Перегрузка функций — это несколько сигнатур вызова, объявленных над одной реализацией, что позволяет одной функции представлять разные комбинации параметров и результата вызывающим. Видны им только сигнатуры перегрузок; сигнатура реализации — часто с более широкими или any типами — недоступна для прямого вызова и должна быть совместима с каждой перегрузкой. Применяй перегрузки, когда результат зависит от формы аргумента, хотя union-тип нередко проще.
Типичные ошибки
- ✗Думать, что сигнатура реализации вызываема — вызывающим видны только сигнатуры перегрузок
- ✗Считать, что перегрузки влияют на рантайм-диспетчеризацию — они стираются и лишь направляют проверку типов
- ✗Браться за перегрузки там, где проще один параметр с union-типом
Уточняющие вопросы
- →Когда стоит предпочесть union-тип перегрузкам?
- →Почему сигнатура реализации должна быть совместима с каждой перегрузкой?
MiddleКодИногдаТипизируйте обёртку debounce, сохраняющую параметры обёрнутой функции
Типизируйте обёртку debounce, сохраняющую параметры обёрнутой функции
Сделайте обёртку обобщённой по типу функции — <T extends (...args: any[]) => void> — и дайте возвращаемой функции тип (...args: Parameters<T>) => void. Вызывающий получит исходный список параметров, проверяемый при каждом вызове, а не any[].
Типичные ошибки
- ✗Типизировать обёрнутую функцию как
Function, теряя всякую проверку параметров - ✗Возвращать
(...args: any[]) => void, из-за чего вызывающий теряет исходную арность - ✗Забывать, что отложенный вызов не может вернуть результат обёрнутой функции
Уточняющие вопросы
- →Как заодно сохранить получателя через
ThisParameterType<T>? - →Что изменится, если отложенный вызов должен вернуть
Promiseс результатом?
MiddleТеорияИногдаКогда набор перегрузок лучше одной сигнатуры с union-параметрами?
Когда набор перегрузок лучше одной сигнатуры с union-параметрами?
Перегрузки нужны, когда тип результата — функция от аргумента: parse(s: string): Node и parse(s: string[]): Node[]. Union-сигнатура вернула бы Node | Node[] и заставила бы вызывающего сужать тип. Когда результат одинаков при любом входе, проще одна union-сигнатура.
Типичные ошибки
- ✗Писать набор перегрузок там, где все сигнатуры возвращают один и тот же тип
- ✗Ждать от union-сигнатуры точного типа результата под каждый вход
- ✗Забывать, что сигнатура реализации не вызываема и должна покрывать все перегрузки
Уточняющие вопросы
- →Как обобщённый параметр с условным типом результата заменил бы эти перегрузки?
- →Почему сигнатуру реализации нельзя вызвать напрямую?
MiddleТеорияИногдаЧто делает объявление this первым параметром функции?
Что делает объявление this первым параметром функции?
Это фиктивный параметр, задающий тип получателя: function onClick(this: HTMLElement, e: Event). Компилятор отвергает вызов, чей this не подходит, а strictBindCallApply распространяет проверку на bind, call и apply. При выпуске он стирается.
Типичные ошибки
- ✗Думать, что вызывающий обязан передать лишний аргумент под параметр
this - ✗Пытаться объявить параметр
thisу стрелочной функции - ✗Ждать, что аннотация привяжет получателя во время выполнения, а не только проверит его
Уточняющие вопросы
- →Что добавляет к этой аннотации флаг компилятора
noImplicitThis? - →Как
ThisParameterType<T>позволяет обёртке пробросить получателя?