Вы столкнулись с менее чем звездной поддержкой TypeScript для того, что я называю коррелированными типами записей . Прямо сейчас он видит, что b.fun
является типом объединения "f1" | "f2"
, а b.value
является типом объединения [string] | [number]
, и он обрабатывает их как некоррелированные . Обычно объединение функций может быть вызвано только с пересечением его параметров , которое в этом случае будет [string] & [number]
. Поскольку [string] | [number]
нельзя присвоить [string] & [number]
, компилятор жалуется. Фактически никакое значение не может быть присвоено [string] & [number]
, поскольку его первый элемент должен быть string & number
, что эквивалентно never
.
, чтобы компилятор мог проверить, что map[b.fun].apply(null, b.value)
является типом безопасно, по сути, потребуется анализировать код несколько раз, по одному для каждого возможного типа b
. Вы можете сделать это явно, дублируя код в операторе switch
/ case
или if
/ else
, но это не происходит автоматически (что имеет смысл, поскольку это приведет к экспоненциальному увеличению времени компиляции, так как количество значений типа union в коде увеличивается), и вы даже не можете попросить компилятор сделать это на добровольной основе .
На данный момент единственный разумный обходной путь - это признать, что вы знаете больше, чем компилятор о безопасности типов кода, и использовать утверждение типа или эквивалент, чтобы сообщить компилятору не беспокойтесь об этом, например:
type MapVals = typeof map[keyof typeof map];
type LooselyTypedMapFunction = (...x: Parameters<MapVals>) => ReturnType<MapVals>
const test1 = (b: MyType) => {
(map[b.fun] as LooselyTypedMapFunction).apply(null, b.value);
};
Здесь тип LooseLyTypedMapFunction
в конечном итоге принимает типы f1
и f2
и объединяет их в одну функцию, которая принимает и производит string | number
. И мы используем утверждение типа от map[b.fun] as LooselyTypedMapFunction
до l ie немного для компилятора. Мы говорим, что map[b.fun]
примет string
, а также number
. Конечно, это неверно, но не столкнется с проблемой, пока вы перейдете в коррелированный список b.value
, а не в какую-нибудь случайную вещь, такую как `[Math.random () <0.5? "упс": 123]). </p>
Это утверждение типа может оказаться более трудоемким, чем вы хотите, но, по крайней мере, оно может заразиться, если вы передадите что-то слишком безумное в map[b.fun]
, так как [string] | [number]
по крайней мере запретит [{foo: string}]
. Вы можете использовать менее безопасное утверждение, подобное этому:
const test2 = (b: MyType) => {
(map[b.fun] as Function).apply(null, b.value);
};
, которое не требует жонглирования типа LooselyTypedMapFunction
, но также будет принимать (map[b.fun] as Function).apply(null, [{foo: 123}]);
. Так что будьте осторожны.
Хорошо, надеюсь, это даст вам некоторое направление. Удачи!
Детская площадка ссылка на код