Напечатайте возможные пути кода против ввода лазейки? - PullRequest
2 голосов
/ 16 марта 2019

учитывая код

class X {a:string = "a"}
class Y {b:number = 1}

// does not compile
function what(v: X|Y) {
  if (v instanceof X) {
    return "its an X";
  }
  if (v instanceof Y) {
    return "its a Y";
  }
  //missing return
}

компилятор машинописного текста говорит:

error TS7030: Not all code paths return a value.

и правильно, так как из-за структурной типизации я мог назвать what как

let v = {b:1}
what(v)

с тех пор v структурно совместим с Y. Теперь я меняю what на

function what(v: X|Y) {
  if ("a" in v) {
    return "its an X";
  }
  if ("b" in v) {
    return "its a Y";
  }
  // missing return
}

и все равно получите ошибку компиляции.

Мне интересно, может ли компилятор просто не получить, что будет взята одна из ветвей if, или есть лазейка, которая все еще позволяет мне передавать совместимый объект, который не соответствует ни одной из двух if ветви. * * 1 021

Ответы [ 2 ]

2 голосов
/ 16 марта 2019

Это ограничение дизайна в 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 | undefinedstring | 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
}

Хорошо, надеюсь, это поможет.Удачи!

1 голос
/ 16 марта 2019

Я знаю другую версию, работающую с интерфейсами и с тегами union. И эта версия компилируется правильно, и весь путь к коду возвращает значение.

interface IX {kind: "a"; a: string }
interface IY {kind: "b"; b: number }

function what2(v: IX|IY): string {
    switch (v.kind) { 
        case "a":
         return "its an X";
         case "b":
         return "its an Y";
    }
}

var r1 = what2({kind: "a", a: "hello"});
var r2 = what2({kind: "b", b: 33});
...