Это ограничение дизайна в TypeScript.Разумно ожидать, что полная проверка заставит компилятор понять, что код после последнего оператора if
недоступен.Но этого не происходит, и не похоже, что они планируют заняться этим в ближайшее время.
Давайте посмотрим, что мы можем с этим сделать сами.В следующем коде:
class X { a: string = "a" }
class Y { b: number = 1 }
// does not compile
function what(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
v // Y
if (v instanceof Y) {
return "its a Y";
}
v // never
}
компилятор фактически сужает тип v
, когда вы передаете эти операторы return
... он жалуется, но не потому, что думает, что v
можетпо-прежнему будет X
или Y
, когда вы упадете в нижней части функции.Он жалуется, потому что анализирует пути кода изолированно от сужения v
, поэтому он видит путь к коду, который неявно возвращает undefined
в конце.Единственный способ исправить это - убедиться, что во всех путях кода есть return
или throw
.
Обратите внимание, что v
сначала сужается до Y
, а затем до never
.Один из способов исправить ошибку - воспользоваться преимуществом сужения до Y
и выполнить return
вместо второго if
теста:
function what(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
v // Y
return "its a Y";
}
Теперь ошибки нет.Если вы доверяете своему чеку instanceof X
, это, вероятно, путь.Другой стандартный способ - использовать вспомогательную функцию проверки исчерпанности assertNever()
:
function assertNever(x: never): never {
throw new Error("OH NOES!!");
}
Функция assertNever()
выдает ошибку, если вы вызываете ее, поэтому возвращает never
.Но он требует, чтобы его параметр имел тип never
, поэтому вызов его должен быть ошибкой компилятора, если код недоступен:
function what(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
v // Y
if (v instanceof Y) {
return "its a Y";
}
v // never
return assertNever(v);
}
Теперь известно, что what()
возвращает string | never
вместо string | undefined
.И string | never
это просто string
.И вы можете увидеть, как она работает как проверка на исчерпывающую полноту, поскольку она выдает ошибку, если вы не сузите v
до never
:
function oops(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
return assertNever(v); // error, v is Y, not never
}
Хорошо, надеюсь, это поможет.Удачи!