Reputation: 824
Why do tests 1 and 2 work here, but test 3 shows a compiler error at foo[barConst]++
: 'Object is possibly "undefined".'? I often need to access properties via bracket notation and thus like to have constants for these properties, but TypeScript doesn't allow this. It also doesn't work with const enum
s. Is it a bug or is there a good reason for the error?
const barConst = 'bar';
interface Foo {
[barConst]?: number;
}
function test1(foo?: Foo) {
if (foo && foo.bar) {
foo.bar++;
}
}
function test2(foo?: Foo) {
if (foo && foo['bar']) {
foo['bar']++;
}
}
function test3(foo?: Foo) {
if (foo && foo[barConst]) {
foo[barConst]++; // compiler error: 'Object is possibly "undefined".'
}
}
Upvotes: 6
Views: 2016
Reputation: 10345
With the release of TypeScript 4.7 due to its improvements to the control flow analysis for bracketed element access, the original code no longer results in a compiler error because the foo && foo[barConst]
guard now correctly narrows the foo[barConst]
access to be of type number
.
See PR #40617 for more details on the CFA update overall.
Upvotes: 3
Reputation: 74500
Narrowing the property access via computed propertyNames/literal expressions seems to be not possible currently. Have a look at this issue and its PR, also that issue.
You can narrow property access in bracket notation with string literals like for example foo["bar"]
. Dynamic expressions like foo[barConst]
don't work. Assigning foo[barConst]
to a variable and working/narrowing down this variable instead is an alternative, but costs an additional declaration.
In your case the simplest solution would be to just cast the expression with non-null assertion operator !
. As you do a pre-check for a falsy value, you are safe here:
function test3(foo?: Foo) {
if (foo && foo[barConst]) {
foo[barConst]!++;
}
}
Upvotes: 2
Reputation: 592
Continue of comment discussions. (I still do not know what are you looking for) you can try something like this:
type barConst = 'bar';
type Foo<K extends string> = Record<K, number>;
function test3(foo?: Foo<barConst>) {
if (typeof foo !== 'undefined') {
foo['bar']++;
}
}
let x: Foo<barConst> = { bar: 2 };
test3(x);
console.log(x);
You can have autocomplete and no compiler error. Still, please Update your question.
Upvotes: 0