Компилятор обычно не определяет типы возвращаемых функций, которые зависят от того, какое конкретное значение было введено в него. Анализ типа потока управления действует для сужения типов конкретных типов переменных внутри реализации функции, поэтому оператор switch
служит для того, чтобы знать, что options
точно равен op2
(например) внутрирелевантный блок case
, но анализ потока управления мало влияет на тип возвращаемого значения функции.
Обычно предполагаемый тип возвращаемого значения функции будет объединением всех типов, return
от функции, независимо от анализа потока управления. Это означает, что подпись test()
подразумевается как function test(options: Options): fn01 | fn02 | fn03 | null
.
Когда вы действительно вызовете функцию типа fn01 | fn02 | fn03 | null
, у вас возникнут проблемы. С опцией --strictNullChecks
компилятора вы не можете вызывать ее вообще (и вам, вероятно, следует использовать опции компилятора --strict
, так как они отлавливают ошибки).
Предполагая, что у вас есть функция типа fn01 | fn02 | fn03
(и вы убедились, что это не null
), вы все равно не можете ее вызвать. Поддержка вызова объединений функций была улучшена на в TypeScript 3.5, но, тем не менее, единственной безопасной вещью, которую вы можете передать объединению функций, является пересечение ее параметров. И пересечение string & string & number
не имеет членов (в JavaScript нет значений, которые являются string
и number
), то есть это never
, и, таким образом, оно полностью не вызывается.
Вот почему это не работает. Чтобы исправить это, вы должны будете использовать перегрузок функций или аннотировать функцию так: generic , где options
имеет универсальный тип O extends Options
.
Перегрузки просты, но на самом деле они не безопасны для типа.
Универсальный тип потенциально может быть более безопасным для типа, но не при использовании switch
, который предоставляет текущее ограничение TypeScript , при котором анализ потока управления не может сузить типы переменных общего типа.
Самый безопасный способ сделать это - использовать объект отображения вместо оператора switch
:
const test = <O extends Options>(options: O) => ({
op1: fn01,
op2: fn02,
op3: fn03
}[options]);
Правильно сделать вывод, что это общая функция, в которой каждый тип ввода соответствует определенному типу выхода функции:
const chosenFN = test('op1'); // (name: string) => void
chosenFN("okay") // okay
test('op2')("age is a string I guess"); // okay
test('op3')(8675309); // okay
Хорошо, надеюсь, это поможет. Удачи!
Ссылка на код