June 30, 2021·4 min read·engineeringtypescriptjsxtooling

TypeScript 4.3.5 repairs JSX auto-import bookkeeping

TypeScript 4.3.5 fixes auto-imports offered inside opening JSX tags. The underlying regression came from changing array positions after metadata had been keyed by those positions.

An editor completion needs more than a symbol name to add a missing import. It also needs to retain information about where that symbol comes from and which edit should accompany the selection. TypeScript 4.3.5 repairs that association for completions inside opening JSX tags. The npm registry records its publication on June 30, and the release note identifies the language-service fix.

The defect is an example of metadata becoming detached from an otherwise valid array. Completion generation still had symbols to examine, but a later change to their positions invalidated the indices used to retrieve associated information. A collection can contain the right items and still lose the relationships needed to turn them into a useful operation.

I regard this as consequential maintenance for a language used heavily in web interfaces. TypeScript's editor support contributes to the quality of that ecosystem. An auto-import failure interrupts a common editing operation even when the project's source can still be made to compile by writing the import manually.

One symbol can have multiple import routes

Andrew Branch's fix description explains the earlier refactoring. Completion sort text and origin maps had changed to use a symbol's index in the symbols array instead of its symbol identifier. The reason was that one symbol can sometimes be imported in more than one way, producing multiple completion entries.

That distinction matters to an import completion. A symbol identity can describe the declaration being suggested without uniquely describing the route used to import it. Two entries associated with that identity may need different origin information. Keying metadata by the entry's position can preserve that distinction, provided the position continues to identify the same entry.

The JSX opening-tag path violated that requirement. It placed intrinsic element symbols at the beginning of the array after other work had associated metadata with indices. Existing entries shifted to new positions, but the corresponding metadata keys did not shift with them. The final completion construction could no longer find the expected origin or sort text.

This explains why the location of the completion mattered. A normal identifier completion did not necessarily pass through the same array manipulation. Opening a JSX tag added a class of candidate symbols that changed the bookkeeping order. A fix aimed only at the visible component declaration would therefore miss the cause.

The patch establishes the prefix before adding other symbols

The implementation diff replaces the late prepend with initialization from intrinsic tag names. Global symbols are then collected into that existing array. Several collection paths change from replacing symbols to concatenating their results with the entries already present.

That ordering keeps the initial prefix in place while subsequent symbol collection proceeds. It avoids moving existing entries after their indices have acquired meaning in the associated maps. The change repairs the relationship between a completion candidate and the metadata used to construct its final entry.

This is a general array-index dependency, but the concrete case is specific. The metadata includes completion sort text and origin information, and the extra prefix contains JSX intrinsic element candidates. Calling it merely a sorting bug would miss the origin lookup needed for an import action.

The patch's scope also distinguishes editor behavior from emitted program behavior. It lives in the language service's completion implementation. Its purpose is to restore the edit offered to the user, not to change the runtime meaning of a completed JSX element or introduce a new component typing rule.

The regression test checks the resulting edit

The added test supplies a component in one file and an incomplete opening JSX tag in another. It asks the completion machinery to apply the import action for that component. The expected result includes the import declaration inserted at the beginning of the edited file.

This checks more than whether the component appears among candidate names. A name without the right origin information is insufficient to produce the import action. Inspecting the resulting file verifies the relationship that the regression broke, including the source module selected for the inserted import.

The incomplete tag is also intentional. Editors request completions while a program is being written, so the language service must provide useful behavior before the file is syntactically complete. Restricting a test to finished source would remove the editing state in which the operation matters.

I prefer this kind of regression test because its assertion describes the user's completed edit. The internal map keys explain the failure, while the expected import explains what the fix restores. That combination makes the maintenance change easier to assess than an assertion about candidate count alone.

TypeScript 4.3.5 restores auto-import behavior through that corrected bookkeeping. The package publication and the release note identify the maintenance version, while the patch shows how JSX-specific candidate collection had shifted indices. The repaired sequence preserves the metadata association needed to turn a component completion into an inserted import.