Skip to content

Array destructuring: string rest element binds a substring instead of an array #753

@nickna

Description

@nickna

Summary

A rest element in an array binding pattern must collect the remainder into a new array (ECMA-262 ArrayBindingPattern). For a string source it instead binds the trailing substring:

const [a, ...rest] = "hello";
console.log(a, rest, Array.isArray(rest));
// SharpTS: h ello false      (rest is the string "ello")
// tsc/JS : h ["e","l","l","o"] true

Both interpreter and compiled mode are affected. Non-rest string destructuring (const [a, b] = "hi") is correct.

Cause

Array destructuring desugars the rest element to _dest0.slice(index) (Parser.Destructuring.cs). For an array source that yields an array; for a string source String.prototype.slice yields a string. String sources stay on the index/slice fast path (they are index-addressable), so the rest binds a substring.

Fix shape

Treat a string source as a materializing iterable in the __arrayDestructure normalization added in #685: type it as string[], materialize to a char array in both Interpreter.NormalizeArrayDestructureSource and the compiled ArrayDestructureSource, and drop string/StringLiteral from the index-addressable pass-through sets in TypeChecker.NormalizeArrayDestructureSourceType and ArrayDestructureHandler. Non-rest string destructuring is unaffected (element types and values are identical); the trade-off is a small char-array copy per string destructure (currently strings stay copy-free on the fast path).

Discovered

While implementing #685. The non-rest string cases are correct, so #685 deliberately scoped strings out as "correct"; this is the residual rest-element gap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions