I’m struggling a bit with checking if the property value of an object is undefined when I access it dynamically through bracket notation. Below is an example of my code.
function toBritishDate(date: Date | string): string {
console.log(date)
return "foo"
}
function myFunc() {
const project: { start_date?: string; end_date?: string } = {
start_date: '2023-09-13',
end_date: '2023-09-29',
};
const array: ("start_date" | "end_date")[] = ['start_date', 'end_date']
array.forEach((element) => {
if (project[element] !== undefined) {
console.log(toBritishDate(project[element]))
}
});
}
You can find a playground on this link.
When calling toBritishDate I get the following error on match.project[element]:
Argument of type ‘string | undefined’ is not assignable to parameter of type ‘string | Date’.
Type ‘undefined’ is not assignable to type ‘string | Date’. (tsserver 2345)
It seems like match.project[element] !== undefined
only checks if the property exists, it doesn’t check if the actual value is undefined. Does anyone know how I can check if the property exists, and that the value is not undefined?
2
Answers
You are correct in your observation. The type of project[element] is inferred as string | undefined, and when you pass it to toBritishDate, TypeScript is rightly complaining because undefined is not assignable to string | Date as expected by toBritishDate.
To fix this, you can use optional chaining (?.) to safely access the property and check if the value is defined before calling toBritishDate. Here’s an updated version of your code:
Note the use of the optional chaining (project[element]?.) before accessing the property, and the non-null assertion operator (!) after project[element] to tell TypeScript that you’re certain the value is not undefined at that point. This helps TypeScript understand that you have checked for the existence of the property and the value. Additionally, I added a key prop to the elements for React’s list reconciliation.
This is a known missing feature, originally filed at microsoft/TypeScript#10530 and now currently tracked at microsoft/TypeScript#56389. TypeScript doesn’t keep track of the identity of a key like
element
when you perform an indexing likeproject[element]
. It only looks at the type, which forelement
is the union type"start_date" | "end_date"
. So it analyzes your code as if it were likewhich is obviously not safe. TypeScript would need to track the identity of the key to tell the difference between your safe code and the above unsafe code. It can’t, right now, so that’s what’s happening.
Until and unless that’s addressed, you’ll need to work around it. The standard workaround is to copy your property into a new variable to sidestep the whole issue around indexing and identity:
Since
projectElement
is just a single variable, the compiler can just perform regular old narrowing analysis on it, which works as you expect.Playground link to code