Проблема в том, что компилятор видит функцию
const f = <K extends keyof NumMap>(key: K) => {
if (key === "one") return 1;
if (key === "two") return 2;
if (key === "three") return 3;
throw new Error("whoops");
}
и может только проверить, что ее возвращаемый тип - 1 | 2 | 3
, а не NumMap[K]
. Как правило, компилятор не использует анализ потока управления для сужения параметров типа, таких как K
. См. microsoft / TypeScript # 13995 . Таким образом, в случае, если key === "one"
, вы могли бы ожидать, тип key
сужаться от K
до "one"
или K & "one"
или что-то, но типа K extends keyof NumMap
сам упорно остается такой же и делает не сужаться до чего-то вроде K extends "one"
. Итак, компилятор недоволен, когда вы используете это вместо того, что принимает NumMap[K]
.
Конкретная ошибка never
вызвана изменением, реализованным в microsoft. / TypeScript # 30769 , чтобы быть более осторожными при попытке присвоить значения переменной индексированного типа, например NumMap[K]
. Если K
является или может быть объединением, например "one" | "two" | "three"
, то компилятор разрешит присваивание только в том случае, если значение является пересечением свойств на этих ключах, или в основном NumMap["one"] & NumMap["two"] & NumMap["three"]
, что является 1 & 2 & 3
, который сворачивается в never
(потому что никакое значение не может быть 1
и 2
и 3
одновременно). Все это означает, что компилятор обеспокоен тем, что вы получаете 1
, 2
или 3
, и это не всегда может быть правильным.
Итак, как же сделать мы это исправим? Самый простой способ - использовать утверждение типа , чтобы сообщить компилятору, что вы знаете, что делаете. Это утомительно, но по крайней мере немного безопасно в том смысле, что он, по крайней мере, будет жаловаться, если вы вернете что-то совершенно нестандартное, например 4
или true
:
const numMapProcAsserts: Proc<NumMap> = {
go: <K extends keyof NumMap>(key: K) => {
if (key === "one") return 1 as NumMap[K];
if (key === "two") return 2 as NumMap[K];
if (key === "three") return 3 as NumMap[K];
throw new Error("whoops");
}
}
Или вы можете использовать одно утверждение any
, чтобы просто заглушить компилятор: заявите, что функция возвращает any
, и реализация, и назначение будут счастливы, но не ожидайте, что компилятор поможет вам, если вы измените * От 1054 * до return "oopsie";
:
const numMapProcOneAssert: Proc<NumMap> = {
go: (key): any => {
if (key === "one") return 1;
if (key === "two") return 2;
if (key === "three") return 3;
throw new Error("whoops");
}
}
Еще одна идея - провести рефакторинг вашей функции, чтобы компилятор действительно мог следовать логике c того, что вы делаете. Если предполагается, что возвращаемый тип является поисковым / индексированным типом доступа, вы можете выполнить фактический индексированный доступ правильного типа, и компилятор будет доволен. В вашем случае, если вам нужен NumMap[K]
, покажите компилятору, который вы индексируете, в объект типа NumMap
с ключом типа K
:
const numMapProcIdx: Proc<NumMap> = {
go: <K extends keyof NumMap>(key: K) =>
(({ one: 1, two: 2, three: 3 } as const)[key])
}
Хорошо , надеюсь, это поможет; удачи!
Детская площадка ссылка на код