In strictNullChecks mode only:
TypeScript Version: nightly (2.0.2)
Code
interface IRoute {
children?: IRoute[];
}
function register(route: IRoute) {
if (route.children) { // <-- route.children is of type IRoute|undefined
Object.keys(route.children).forEach(routeKey => { // <-- route.children is of type IRoute
register(route.children[routeKey]); // <-- Error: Object 'route.children' is possibly 'undefined'.
});
}
}
Expected behavior:
There shouldn't be an error here, because route.children
is being checked 2 lines above.
Actual behavior:
TypeScript fails to recognize route.children
cannot be undefined anymore, because of the nested function. It is the same behavior with both function declaration and arrow functions.
This is the intended behavior because we don't know that route.children
is _still_ not-undefined at the point when the callback executes. It is the case that forEach
executes its callback immediately, but this is not true of all functions and we don't know that someone isn't going to remove route.children
after you leave the scope of register
.
A good solution is to capture the property in a const, which we can statically know will still be not-undefined
function register(route: IRoute) {
const children = route.children;
if (children) { // <-- children is of type IRoute|undefined
Object.keys(children).forEach(routeKey => { // <-- children is of type IRoute
register(children[routeKey]); // <-- OK
});
}
}
Thanks @RyanCavanaugh for the fast feedback :) That's true! I never thought of that possibility.
I just enabled strictNullChecks for our project and found these 3 issues (2 I posted earlier) that I couldn't wrap my head around. Truly amazing work :+1:
@RyanCavanaugh for some reason this is not working for 2.0.10 and 2.1+ for me right now.
There got to be something on my side as it was working....
let deferred = defer()
setTimeout(() => {
if (deferred) {
deferred.resolve(1) // <-- still error with Object is possibly 'undefined'
}
}, 1)
Need some help. 馃檱
@unional please post question on Stack Overflow and/or provide a self-contained example
Yes. I can reproduce it:
// index.ts
export function defer() {
let resolve
let reject
let promise = new Promise((r, j) => {
resolve = r;
reject = j;
});
return {
resolve: resolve,
reject: reject,
promise: promise
};
}
let deferred = defer()
setTimeout(() => {
if (deferred !== undefined) {
deferred.reject(1) // <-- in VSC there is no error, but compiles fail
}
}, 1)
// tsconfig.json
{
"compilerOptions": {
"lib": [
"es2015",
"dom"
],
"strictNullChecks": true,
"target": "es5"
},
"include": [
"index.ts"
]
}
error:
index.ts(18,5): error TS2532: Object is possibly 'undefined'.
Tested with [email protected]
And it turns out the issue is fixed in [email protected] and does not exist in 2.0.10
Have the same issue with typescript (tsc) version 2.3.4
with the process module.
process.on('message', (msg) => {
console.log('Message from parent:', msg);
});
but doing:
process.send()
does not compile saying:
error TS2532: Object is possibly 'undefined'.
Happens to me too :
Vue.directive('click-outside', {
bind: (el, binding, vnode) => {
const event = ..a function
if (vnode) {
vnode.context[binding.expression](event) // Object is possibly 'undefined'.ts(2532)
}
}
}
Both codium & compile shows the error. Issue being tracked at #10642
Most helpful comment
This is the intended behavior because we don't know that
route.children
is _still_ not-undefined at the point when the callback executes. It is the case thatforEach
executes its callback immediately, but this is not true of all functions and we don't know that someone isn't going to removeroute.children
after you leave the scope ofregister
.A good solution is to capture the property in a const, which we can statically know will still be not-undefined