Skip to content

xs["0"] reads undefined on a string-valued receiver whose declared type is an array #7891

Description

@proggeramlug

Summary

xs["0"] answers undefined where JavaScript answers the character, when xs is
declared string[] (or any array type) but holds a string at runtime. The
numeric form xs[0] is correct. The same read written directly on an any is correct.
So the wrong answer is selected by the annotation, not by the value.

Repro

type Bag = { items: string[] };
function mk(v: any): Bag { return { items: v }; }
function viaDeclared(b: Bag): string { return "" + b.items["0"] + "/" + b.items[0]; }
const s: any = "ss";
console.log(viaDeclared(mk(s)));      // node: s/s     perry: undefined/s
const direct: any = "ss";
console.log("" + direct["0"] + "/" + direct[0]);  // node: s/s   perry: s/s

test-files/test_gap_7891_string_receiver_numeric_string_key.ts.

Why

A declared array type is an erased annotation, not a runtime proof, so the value can be
anything. is_array_expr admits the receiver to expr/index_get.rs's array arm, and that
arm's static-string-key route is
js_array_get_index_or_stringarray_get_property_by_key
js_object_get_field_by_name — which has no string-receiver index arm and returns
undefined. The numeric route is fine: js_array_get_f64 classifies the receiver
(clean_arr_ptr / array_object_receiver) and handles a string correctly.

So the array arm's two key routes have different receiver-validation strength, and only
the numeric one is claim-safe.

Scope / severity

Pre-existing on main @ 564bd997b, reachable through a non-union declared receiver
(a plain b: Bag parameter, no union, no reassignment). Silent wrong answer, no crash.

Found while adding coverage for #7890, which is why #7890 deliberately refuses to
widen the set of receivers that reach this route: its claim is restricted to a
non-string, non-symbol key so a string key keeps exactly the path it has today.

Suggested fix

Either give array_get_property_by_key the same receiver classification the numeric
route has, or refuse the array arm's static-string-key route for any receiver whose
array type is a claim rather than a proof.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions