typescript-go icon indicating copy to clipboard operation
typescript-go copied to clipboard

Fix completions crash and enable contextual type completions in array literals

Open Copilot opened this issue 3 weeks ago • 6 comments

Completions panic with "index out of range [-1]" when requested inside new Map([...]) constructor at certain positions:

const m = new Map([
    [/*crash here*/'0', ['0', false]],
]);

Additionally, string literal completions were not being suggested in tuple contexts without typing a quote character first.

This PR also fixes an edge case bug where properties named "-1" would incorrectly be suggested as completions in array literals due to the -1 index being used to look up property names.

Changes

  • completions.go: Add special handling for OpenBracketToken and CommaToken in array literals

    • When previousToken is [, get contextual type for element at index 0
    • When previousToken is ,, count elements before cursor and get contextual type for next element
    • Prevents invalid tokens (which aren't array elements) from reaching checker's contextual type resolution
  • services.go: Add GetContextualTypeForArrayElement function

    • Exported wrapper around getContextualTypeForElementExpression for language service use
    • Enables proper contextual type resolution for array elements at specific indices
  • Tests: Added TestCompletionsInMapConstructorNoCrash and TestCompletionsInArrayLiteralWithContextualType

    • Verifies no crash at reported positions
    • Validates that string literal completions work in tuple contexts without typing quotes
    • Confirms that the -1 property edge case is fixed (properties named "-1" are not suggested)

Benefits

  1. Fixes the crash: Language service no longer passes non-element tokens to checker
  2. Enables better completions: String literal completions now work in tuple contexts without needing to type a quote first (e.g., let y: ["foo" | "bar"] = [/*here*/] now suggests "foo" and "bar")
  3. Fixes edge case bug: Properties named "-1" are no longer incorrectly suggested in array literals (e.g., let x: { "-1": "hello" } = [/**/] no longer suggests "hello")
  4. Better architecture: Language service handles token-level concerns, checker handles type-level concerns
Original prompt

This section details on the original issue you should resolve

<issue_title>Completions crash in call to new Map(...).</issue_title> <issue_description>```ts const m = new Map([ [/a/'0', ['0', false]]/b/, ]);


Request completions at `/*a*/` or `/*b*/`.

[error] panic handling requesttextDocument/completionruntime error: index out of range [-1]goroutine 3826 [running]: runtime/debug.Stack() runtime/debug/stack.go:26 +0x64 github.com/microsoft/typescript-go/internal/lsp.(*Server).recover(0x4000198008, 0x4019948ff0) github.com/microsoft/typescript-go/internal/lsp/server.go:872 +0x40 panic({0x7ff7c70f3b80?, 0x40177be4b0?}) runtime/panic.go:783 +0x120 github.com/microsoft/typescript-go/internal/checker.(*Checker).getContextualTypeForElementExpression.func1(0x401a5d2ee0) github.com/microsoft/typescript-go/internal/checker/checker.go:28915 +0x29c github.com/microsoft/typescript-go/internal/checker.(*Checker).mapTypeEx(0x4005641e60?, 0x4018009db0?, 0x40178d3158?, 0x94?) github.com/microsoft/typescript-go/internal/checker/checker.go:24914 +0xd4 github.com/microsoft/typescript-go/internal/checker.(*Checker).getContextualTypeForElementExpression(0x40106ea608?, 0x4018009d60?, 0x2?, 0x4?, 0x401978a6c0?, 0x7ff7c73b4af8?) github.com/microsoft/typescript-go/internal/checker/checker.go:28910 +0x3c github.com/microsoft/typescript-go/internal/checker.(*Checker).getContextualType(0x40106ea608, 0x401978a6c0, 0x4) github.com/microsoft/typescript-go/internal/checker/checker.go:28328 +0x1e0 github.com/microsoft/typescript-go/internal/checker.(*Checker).GetContextualType.func1() github.com/microsoft/typescript-go/internal/checker/services.go:310 +0x24 github.com/microsoft/typescript-go/internal/checker.runWithoutResolvedSignatureCaching[...](0x40106ea608?, 0x4018009db0, 0x400083f4a8?) github.com/microsoft/typescript-go/internal/checker/services.go:365 +0x2f0 github.com/microsoft/typescript-go/internal/checker.runWithInferenceBlockedFromSourceNode[...](0x40106ea608?, 0x401978a6c0, 0x400083f4a8?) github.com/microsoft/typescript-go/internal/checker/services.go:329 +0xe4 github.com/microsoft/typescript-go/internal/checker.(*Checker).GetContextualType(0x400083f620?, 0x400083f5c0?, 0x83f574?) github.com/microsoft/typescript-go/internal/checker/services.go:310 +0x48 github.com/microsoft/typescript-go/internal/ls.getContextualType(0x401978a6c0, 0x400c205808?, 0x40106ea608?, 0x40106ea608) github.com/microsoft/typescript-go/internal/ls/completions.go:2995 +0x2a0 github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionData(0x40097bbe90, {0x7ff7c74e2b88, 0x40097bbda0}, 0x40106ea608, 0x400c205808, 0xb8, 0x400326e800) github.com/microsoft/typescript-go/internal/ls/completions.go:1727 +0x1038 github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionsAtPosition(0x40097bbe90, {0x7ff7c74e2b88, 0x40097bbda0}, 0x400c205808, 0xb8, 0x0) github.com/microsoft/typescript-go/internal/ls/completions.go:383 +0x22c github.com/microsoft/typescript-go/internal/ls.(*LanguageService).ProvideCompletion(0x40097bbe90, {0x7ff7c74e2b88, 0x40097bbda0}, {0x400dc60000?, 0x40097bbda0?}, {0xdc60000?, 0x40?}, 0x4012c7a830) github.com/microsoft/typescript-go/internal/ls/completions.go:44 +0xa0 github.com/microsoft/typescript-go/internal/lsp.(*Server).handleCompletion(0x400443fe78?, {0x7ff7c74e2b88?, 0x40097bbda0?}, 0x400dc60000?, 0x400443fee8?) github.com/microsoft/typescript-go/internal/lsp/server.go:1208 +0x3c github.com/microsoft/typescript-go/internal/lsp.init.func1.registerLanguageServiceDocumentRequestHandler[...].16({0x7ff7c74e2b88, 0x40097bbda0}, 0x4019948ff0) github.com/microsoft/typescript-go/internal/lsp/server.go:621 +0xec github.com/microsoft/typescript-go/internal/lsp.(*Server).handleRequestOrNotification(0x4000198008, {0x7ff7c74e2bc0?, 0x401e249220?}, 0x4019948ff0) github.com/microsoft/typescript-go/internal/lsp/server.go:502 +0x128 github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop.func1() github.com/microsoft/typescript-go/internal/lsp/server.go:405 +0x34 created by github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop in goroutine 20 github.com/microsoft/typescript-go/internal/lsp/server.go:425 +0x7a0


## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>

  • Fixes microsoft/typescript-go#2254

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot avatar Dec 06 '25 17:12 Copilot