Typescript - встроенная неопределенная проверка не работает (возможно, объект «undefined».ts(2532))

Я получаю следующую ошибку машинописного текста:

const myFunction = (
  param1: string | undefined,
  param2: { someProp: string } | undefined
) => {
  if (!param1 && !param2) {
    return;
  }

  // Here I get the following Typescript error:
  //  (parameter) param2: { someProp: string } | undefined
  //  Object is possibly 'undefined'.ts(2532)
  const param3 = param1 ? param1 : param2.someProp;
};

следующие работы:

const param4 = param1 ? param1 : param2 ? param2.someProp : null;

но кажется излишним дважды проверять значение null или undefined.

Я должен упомянуть, что для параметра strictNullChecks установлено значение true в параметрах компилятора, и я хочу оставить его таким.

Любая идея, почему я получаю эту ошибку?

Вот CodeSandbox с кодом: https://codesandbox.io/s/jn2mp01q2v


person Sergiu    schedule 03.05.2019    source источник
comment
the following works ... it doesn't work если strictNullChecks установлено на true   -  person bugs    schedule 03.05.2019
comment
@bugs извините, мой плохой. Вам нужно указать правильный тип. Пожалуйста, взгляните на ссылку CodeSandbox: codesandbox.io/s/jn2mp01q2v.   -  person Sergiu    schedule 03.05.2019


Ответы (2)


Печальная правда о компиляторе TypeScript заключается в том, что он просто не так умен, как человек (во всяком случае, с TypeScript 3.4), и поэтому его анализ потока управления — это лишь бледная тень того анализа, который вы можете выполнить самостоятельно. Конечно, он очень последователен в своем анализе, в то время как мой имеет тенденцию ухудшаться, когда я не ел в последнее время.

Если вы выполняете проверку переменной типа объединения, которая полностью исключает одну или несколько составляющих этого объединения, компилятор с радостью уменьшит для вас тип переменной:

param1.charAt(0); // error, possibly undefined
if (!param1) return;
param1.charAt(0); // okay now

Но одна вещь, которую компилятор просто не делает, — это отслеживание коррелированных переменных за пределами дискриминированные союзы. Что вы устранили, проверив

if (!param1 && !param2) return;

это возможность того, что и param1, и param2 могут быть undefined одновременно. Вы взяли две ранее независимые переменные и связали их друг с другом. Которые компилятор не отслеживает. Поскольку param1 и param2 могут оба по-прежнему быть undefined (только не одновременно), компилятор рассматривает их как все еще независимые, и вы остаетесь со своей проблемой.

Вы можете сделать то, что предложил другой ответ, и использовать утверждение типа, который предназначен для случаев, когда вы умнее компилятора и не хотите пытаться провести компилятор через задачу понимания того, что вы уже знаете:

const param3 = param1 ? param1 : param2!.someProp; // I'm smarter than the compiler ????

Обратите внимание, что я использовал не- оператор нулевого утверждения !, который, вероятно, является наиболее лаконичным способом подтвердить свое превосходство над машиной. Также имейте в виду, что такие утверждения небезопасны, так как вы можете заблуждаться в отношении своего превосходства. Так что делайте что-то подобное только после того, как дважды и трижды проверите, что param2 никак не может быть undefined.


Еще одна вещь, которую вы можете сделать, — это реструктурировать свой код, чтобы провести компилятор через анализ, который он может выполнить.

const param3 = param1 || (param2 ? param2.someProp : undefined);
if (!param3) return;
param3.charAt(0); // string

Это будет работать для вас, и вы проверяете каждый параметр только один раз. Переменная param3 имеет тип string | undefined и имеет значение undefined только в том случае, если и param1, и param2 ложны. Каждый шаг в этом коде полностью устраняет составляющие объединения для каждой переменной, не оставляя никаких коррелированных типов, которые могли бы запутать компилятор.

Любое решение должно работать для вас. Надеюсь, это поможет; удачи!

person jcalz    schedule 03.05.2019
comment
Это отличный ответ! Я искал объяснение того, почему компилятор Typescript не улавливает этот случай, и ваш ответ проясняет это. Благодарю вас! - person Sergiu; 04.05.2019

Поскольку вы указали два типа для param2, один из них является объектом, а другой не определен. Вы должны дать как

константа параметр3 = параметр1 ? param1 : (param2 как любой).someProp;

то он должен работать.

person Atul Bansode    schedule 03.05.2019
comment
Я подозреваю, что это работает, но мой вопрос был в том, почему я должен дважды проверять объект param2? - person Sergiu; 03.05.2019