'TypeScript(err:2532): object is possibly undefined dispite null/undefined check
I've been writing a Discord-bot in TypeScript and I added a command that retrieves information from an airport. The only thing you have to provide is the ICAO code of the airport(4 character code that identifies the airport). Now: Obviously users might get the ICAO code wrong and maybe give invalid ones. So that's why I have a method that retrieves the airport object from a JSON-File and looks a bit like this(not an exact replica, but just so you get an idea of it):
public getAirport(icao: string): Airport | undefined {
return arrayOfAllAirports.find(ap => ap.icao === icao);
}
In my command file/class I use this method to obviously retrieve the airport with the given ICAO code. Then I check if its return value is undefined and if so, I return *some error*
and that is that. And that looks kind of like this:
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
But in two files/classes in which I use the said method together with the said checks I get an error further down, that the object might be undefined. Now comes the even more confusing bit: A few lines lower. I access some property of 'airport' again and it doesn't say anything.
Now I know I could use as Airport
when I retrieve the airport or reassign it etc, but I want a proper solution and not cheat typescript in stupid ways. Do any of you have an Idea on how to get hold of this issue?
edit:
here's the picture of where it worked: https://i.stack.imgur.com/tTZyJ.png
Solution 1:[1]
The problem is that you are using let
, not const
. let
can be reassigned which means that every time there was a chance it might have been changed, its type will be broadened to its original type. Something like this:
// 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;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |