Reputation: 31
I saw this at work recently and I was wondering why Typescript doesn't understand the code below. If test is either undefined or a string, shouldn't it understand that it has to be a string if it's in an if statement?
interface IObj {
test?: string;
}
const obj: IObj = {
test: "why does this not work?",
};
if (obj.test) {
const { test } = obj;
() => testFunc(obj.test); // error (why does this not work?)
() => testFunc(test); // works
}
function testFunc(testString: string) {
return testString;
}
Destructuring works, but not if you use the object and value right away.
Upvotes: 2
Views: 484
Reputation: 297
An attribute with ?
(like test in IObj
) has the possibility of being undefined, so the type of test
is string | undefined
.
When you check for the existence of the test
attribute in your if statement, you are explicitly saying that test
must not be undefined to enter the block. Accessing obj.test
or your newly created variable test
should return a string.
However, the problem is that you are using obj.test
inside an arrow function, which creates a new scope and loses the validation you did earlier. Basically, for typescript, there's no way to guarantee that obj.test
won't be undefined when the function is called.
This problem does not occur with the test
variable because when you create it inside the if statement, the obj.test
is a string
, so test is also a string
, with no possibility of undefined
.
Consider the following example:
if (obj.test) {
const { test } = obj;
setTimeout(() => {
testFunc(obj.test);
testFunc(test);
}, 5000);
obj.test = undefined;
}
setTimeout
also creates another scope and, as you might know, it will execute the function after X
milliseconds (5 seconds in this case). Since it's not blocking, the flow of execution will reach obj.test = undefined
before testFunc(obj.test)
within setTimeout
. This is an example of a problem that can occur.
Upvotes: 4
Reputation: 19381
Since typescript 4.9 you can use the new satisfies operator instead of the type-assertion:
interface IObj {
test?: string;
}
const obj = {
test: "why does this not work?",
} satisfies IObj;
if (obj.test) {
const { test } = obj;
() => testFunc(obj.test); // works
() => testFunc(test); // works
}
function testFunc(testString: string) {
console.log(testString);
return testString;
}
Upvotes: 0