Предикаты типов в TypeScript

Это недостаток функции в TypeScript или что-то хорошо продуманное, что компилятор TS не может вывести (а затем сузить) тип аргумента, если логика условия заключена в отдельную функцию? Я имею в виду, что в этом случае компилятор TS выдает мне ошибку:

function isString(s) {
  return typeof s === 'string';
}

function toUpperCase(x: unknown) {
  if(isString(x)) {
    x.toUpperCase(); // ⚡️ x is still of type unknown
  }
}

Однако, если я избавлюсь от функции isString и встрою ее логику в оператор if, тогда компилятор TS хорошо узнает тип.

Я знаю, что могу привести тип с помощью as string или использовать предикаты типа и использовать аннотацию типа is string в функции isString. Но я только начал думать - почему ТС не может сузить тип x.


person leszczu450    schedule 26.09.2019    source источник


Ответы (1)


Один из способов, чтобы это работало, состоял бы в том, чтобы компилятор встраивал некоторые вызовы функций при выполнении анализа потока управления, что означает, что это:

function toUpperCase(x: unknown) {
  if (isString(x)) {
     x.toUpperCase(); 
  }
}

нужно будет проанализировать, как если бы тело isString() было расширено следующим образом:

function toUpperCase(x: unknown) {
  if (typeof x === "string") {
    x.toUpperCase();
  }
}

Каноническая проблема в GitHub, говорящая об этом, называется microsoft/TypeScript#9998. Вопрос, заданный в этом выпуске, звучит так: «Когда вызывается функция, каковы, по нашему мнению, ее побочные эффекты?» И, кажется, нет идеальных ответов. Говорят, что полное встраивание для всех вызовов функций «было бы даже отдаленно непрактичным». Можно ли сделать неглубокое встраивание, которое бы вело себя так, как вы хотите, для isString(), не перегружая полностью компилятор? Возможно, но стоит ли? Не уверена. Не похоже, чтобы там был достигнут большой прогресс; если вы достаточно твердо настроены и у вас есть убедительные идеи, вы можете внести свой вклад в проблему GitHub.


Другим способом для этого было бы, если бы компилятор автоматически выводил возвращаемые типы предикатов типа для правильных видов boolean-возвращающих функций, что означает, что это:

function isString(s: unknown) {
  return typeof s === "string";
}

нужно было бы сделать вывод, как если бы это было так:

function isString(s: unknown): s is string {
  return typeof s === "string";
}

Каноническая проблема в GitHub, говорящая об этом, называется microsoft/TypeScript#16069. Предыдущая версия была отклонена как слишком сложная, потому что опять же, для компилятора нецелесообразно анализировать каждую функцию, возвращающую логическое значение, и выяснять, есть ли какие-либо последствия, подобные предикату типа, для ее возвращаемого типа. Тем не менее, #16069 все еще открыт, по-видимому, с намерением собрать низко висящие плоды "простых" функций, таких как x => typeof x === "string". И опять же, неясно, есть ли там какой-либо прогресс, поэтому, если вы чувствуете себя достаточно уверенно и у вас есть убедительные идеи, вы можете внести свой вклад в проблему GitHub.


Но ни в том, ни в другом случае я бы не стал задерживать дыхание на твоем месте. Идиоматическим решением для TS3.6 было бы просто аннотировать isString() как определяемую пользователем защиту типа, возвращающую s is string, и переходить к другим вещам.

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

person jcalz    schedule 27.09.2019