Say we have this typescript code:
interface A {
bar: string;
}
const isA = (obj: T): obj is T & A => {
obj[\'bar\'] = \'world\';
return tr
The general answer is that you are cleverer than the compiler in this case. TypeScript uses heuristics to analyze the control flow of the code to try to infer narrower types for expressions. It does a reasonable job of this, but it isn't perfect and probably can never be.
When you access obj.bar
immediately after throwing if isA(obj)
returns false
, the compiler narrows obj
to include A
, as you expect. Unfortunately, when you create a closure and pass it to Array.prototype.forEach()
, the compiler widens obj
back to its original type which does not include A
. Now you and I know that forEach()
will immediately invoke its callback function, but TypeScript doesn't. For all it knows, the value of obj
will be modified before the callback is invoked. And there's no way to tell the compiler differently. So it decides that narrowing isn't safe and gives up.
So, workarounds: one idea is to make obj
a const
instead of declaring it with let
:
const obj = { foo: 'hello' };
if (!isA(obj)) throw 'wont ever throw'
Array(5).fill(0).forEach((_, i) => {
obj.bar // This is okay now
});
This doesn't actually change the fact that obj.bar
can be added or removed before a callback that closes over obj
is called, but the heuristic that TypeScript uses is something like "const
is more likely to be immutable than let
", even though it's not, really.
A similar workaround, if you can't make obj
a const
, is to assign a new const
variable after the narrowing takes place, and use that in the callback instead:
let obj = { foo: 'hello' };
if (!isA(obj)) throw 'wont ever throw'
const myObj = obj;
Array(5).fill(0).forEach((_, i) => {
myObj.bar // This is okay
});
Of course by that token you might as well skip the obj
middleman entirely:
let obj = { foo: 'hello' };
if (!isA(obj)) throw 'wont ever throw'
const bar = obj.bar;
Array(5).fill(0).forEach((_, i) => {
bar // This is okay
});
It's up to you. Hope that helps. Good luck!