TypeScript (err: 2532): объект, возможно, не определен, несмотря на проверку null / undefined - PullRequest
0 голосов
/ 09 июля 2020

Я писал Discord-бота на TypeScript и добавил команду, которая получает информацию из аэропорта. Единственное, что вам нужно предоставить, это код аэропорта ИКАО (4-значный код, который идентифицирует аэропорт). Теперь: очевидно, что пользователи могут ошибаться в коде ИКАО и, возможно, давать неверные. Вот почему у меня есть метод, который извлекает объект аэропорта из JSON -файла и выглядит примерно так (не точная копия, но просто чтобы вы поняли это):

    public getAirport(icao: string): Airport | undefined {
        result = arrayOfAllAirports.find(ap => ap.icao === icao);
        return result;
    }

В моем командном файле / классе я использую этот метод для очевидного получения аэропорта с заданным кодом ИКАО. Затем я проверяю, не определено ли его возвращаемое значение, и если да, я return *some error* и все. И это выглядит примерно так:

    let airportMgr = new AirportManager();
    
    let airport = airportMgr.getAirport(icao);
    if (airport === undefined) {
        return *invalid icao error*;
    }

    *blablabla*

    let exampleString = `ICAO: ${airport.icao} | Name: ${airport.name}`;
                                   ^error here              ^error here

Но в двух файлах / классах, в которых я использую указанный метод вместе с указанными проверками, я получаю сообщение об ошибке ниже, что объект может быть неопределенным. А теперь еще более запутанный момент: несколькими строками ниже. Я снова получаю доступ к некоторому свойству 'airport', и он ничего не говорит.

Теперь я знаю, что могу использовать as Airport, когда возвращаю аэропорт или переназначаю его и т.д. c, но мне нужен правильный решение, а не обмануть машинописный текст глупыми способами. У кого-нибудь из вас есть идея, как решить эту проблему?

редактировать:

вот изображение того, где это сработало: https://i.stack.imgur.com/tTZyJ.png

1 Ответ

0 голосов
/ 09 июля 2020

Проблема в том, что вы используете let, а не const. let можно переназначить, что означает, что каждый раз, когда есть вероятность, что он мог быть изменен, его тип будет расширен до исходного. Примерно так:

// I made it async to demonstrate something below, for your case it is not important
async function doStuff() {
  // Some icao code
  const icao = 'some icao';
  let airport: Airport | undefined = getAirport(icao);

  // Let's start by checking whether the airport is undefined
  if (airport === undefined) {
    return;
  }

  // And now let's break down all the scenarios that work
  // and then all the ones that don't
  //
  // The important concept here is called lexical scope,
  // an "environment" in which variables and constants are defined

  // 1. current lexical scope - the scope of this function
  //
  // This should work since the value of airport has just been checked
  // and has not had an opportunity to be changed
  airport.name;

  // 2. "child" lexical scope - scope created by a function defined within
  // the current lexical scope
  // 
  // This should not work since you might be calling this function
  // some later time, when the value of airport would have possibly changed
  function someHelperFunction() {
    airport.name;
  }

  // The same applies to promise resolution values
  Promise.resolve().then(() => {
    // This should not work
    airport.name
  });

  // It works with simple awaits
  const anything = await Promise.resolve('something');
  // This will work
  airport.name;

  // A TRICK (!!!)
  //
  // You can at this point create a const of type Airport (because now you are sure
  // it is an airport) and use that in your functions.
  //
  // Since it is defined as const, it cannot be changed 
  // and it will stay an airport like forever and ever
  const airportForSure: Airport = airport;
  function someOtherHelperFunction() {
    // WORKS!!!
    airportForSure.name;
  }

  Promise.resolve().then(() => {
    // Yesss
    airportForSure.name
  });

  // Then when you change the value of airport, you need to check for undefined again
  airport = getAirport(icao);

  // This will not work
  airport.name;
}

type Airport {
  icao: string;
  name: string;
}

// Just a placeholder for a function
declare function getAirport(icao: string): Airport | undefined;

Ссылка на площадку TypeScript

...