Reputation: 40561
The following code results in the error:
example/not-following.ts:15:1 - error TS2722: Cannot invoke an object which is possibly 'undefined'.
15 run(true).maybe();
~~~~~~~~~~~~~~~
Code:
interface Something {
maybe?: () => void;
}
function run(isTrue: boolean): Something {
const object: Something = {};
if (isTrue) {
object.maybe = (): void => {
console.log('maybe');
};
}
return object;
}
run(true).maybe();
Since this is deterministic code, why doesn't TypeScript follow it?
Upvotes: 2
Views: 568
Reputation: 40561
The solution was something called generics
: https://www.typescriptlang.org/docs/handbook/generics.html
interface Something {
maybe?: () => void;
}
function run<T extends boolean>(isTrue: T):
T extends true
? Something & Required<Pick<Something, "maybe">>
: Something {
const object: Something = {};
if (isTrue) {
object.maybe = () => {
console.log('maybe');
};
}
// @ts-ignore
return object;
}
run(true).maybe();
// @ts-expect-error
run(false).maybe();
Upvotes: 3
Reputation: 3905
I guess TypeScript is pretty smart, but probably not so smart that it completely validates these kind of runtime execution paths.
As an alternative, you could make use of closures, but I'm not sure if such a solution is appropriate for you.
interface Something {
maybe: () => void;
}
function run(isTrue: boolean): Something {
const object: Something = {
maybe: () => {
if (isTrue) {
console.log('maybe');
}
}
};
return object;
}
run(true).maybe();
Edit:
Due to the downvote I received (which I do not want to object to), I want to additionally remark that for avoiding the error in TypeScript, you need to either:
maybe
function mandatory orI personally dislike optional fields and I personally do not want to tediously wrap each call in a checking if
-block. So I personally still prefer my current solution.
Upvotes: 0
Reputation:
Cannot invoke an object which is possibly 'undefined'.
You get this error because maybe
is optional, typescript has no way of knowing whether maybe
is defined or not.
To fix it you'll have to check if the property exists first:
interface Something {
maybe?: () => void;
}
function run(isTrue: boolean): Something {
const object: Something = { };
if (isTrue) {
object.maybe = (): void => {
console.log('maybe');
};
}
return object;
}
const oSomething: Something = run(true);
if (oSomething.maybe) {
oSomething.maybe();
}
Upvotes: 0