Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 18, 2025

Fixes #2121

When a type predicate references a non-existent parameter name, parameterIndex is set to -1. Flow analysis code attempted to index into arguments arrays without bounds checking, causing a panic.

Changes

  • getTypePredicateArgument: Added parameterIndex >= 0 check before array access (handles regular type predicates like value is Type)
  • isReachableFlowNodeWorker: Added parameterIndex >= 0 check in assertion predicate handling (handles assertion predicates like asserts condition when checking for unreachable code)
  • Test coverage: Added typePredicateParameterMismatch.ts and assertsPredicateParameterMismatch.ts test cases in testdata/tests/cases/compiler/ to verify both code paths

Examples

Regular type predicate:

function isTypeA(
  _value: UnionType
): value is TypeA {  // "value" doesn't match "_value"
  return true;
}

Assertion predicate:

function assertCondition(
  _condition: boolean
): asserts condition {  // "condition" doesn't match "_condition"
  if (!_condition) {
    throw new Error('Condition failed');
  }
}

Before: panic: runtime error: index out of range [-1]

After: error TS1225: Cannot find parameter 'value'. (matches tsc behavior)

Original prompt

This section details on the original issue you should resolve

<issue_title>Panic: index out of range [-1] on type predicate parameter mismatch</issue_title>
<issue_description>## Description

tsgo panics when a type predicate references a parameter name that doesn't match the function parameter.

Reproduction

type TypeA = { kind: 'a' };
type TypeB = { kind: 'b' };
type UnionType = TypeA | TypeB;

function isTypeA(
  _value: UnionType
): value is TypeA {  // "value" doesn't match parameter "_value"
  return true;
}

function test(input: UnionType): void {
  if (isTypeA(input)) {
    console.log(input.kind);
  }
}

Run: tsgo --noEmit

Expected

TypeScript error or graceful handling (like tsc does).

Actual

npx tsgo --noEmit --project tsconfig.json
panic: runtime error: index out of range [-1]

goroutine 231 [running]:
github.com/microsoft/typescript-go/internal/checker.(*Checker).getTypePredicateArgument(0xc000e20500?, 0xc000f38ae0, 0xc00014e098?)
        github.com/microsoft/typescript-go/internal/checker/flow.go:2409 +0xac
github.com/microsoft/typescript-go/internal/checker.(*Checker).narrowTypeByTypePredicate(0xc0005a1908, 0xc000316ae0, 0xc00062e900, 0xc000f38ae0, 0x7f360cdde5c0?, 0x1)
        github.com/microsoft/typescript-go/internal/checker/flow.go:307 +0x75
github.com/microsoft/typescript-go/internal/checker.(*Checker).narrowTypeByCallExpression(0xc0005a1908, 0xc000316ae0, 0xc00062e900, 0xc000152000, 0x1)
        github.com/microsoft/typescript-go/internal/checker/flow.go:442 +0xf0
github.com/microsoft/typescript-go/internal/checker.(*Checker).narrowType(0xc0005a1908, 0xc000316ae0, 0xc00062e900, 0xc000152000, 0x1)
        github.com/microsoft/typescript-go/internal/checker/flow.go:390 +0x254
github.com/microsoft/typescript-go/internal/checker.(*Checker).getTypeAtFlowCondition(0xc0005a1908, 0xc000316ae0, 0xc0002eaf20)
        github.com/microsoft/typescript-go/internal/checker/flow.go:356 +0x106
github.com/microsoft/typescript-go/internal/checker.(*Checker).getTypeAtFlowNode(0xc0005a1908, 0xc000316ae0, 0xc0005a85b0?)
        github.com/microsoft/typescript-go/internal/checker/flow.go:154 +0x412
github.com/microsoft/typescript-go/internal/checker.(*Checker).getFlowTypeOfReferenceEx(0xc0005a1908, 0xc00014e170, 0xc00062e900, 0xc00062e900, 0xc00014a0b0, 0xc00012216c?)
        github.com/microsoft/typescript-go/internal/checker/flow.go:96 +0x18f
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkIdentifier(0xc0005a1908, 0xc00014e170, 0x0)
        github.com/microsoft/typescript-go/internal/checker/checker.go:10851 +0xe9f
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkExpressionWorker(0xc0005a1908, 0xc00014e170, 0x14e0e0?)
        github.com/microsoft/typescript-go/internal/checker/checker.go:7515 +0x1af
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkExpressionEx(0xc0005a1908, 0xc00014e170, 0x0)
        github.com/microsoft/typescript-go/internal/checker/checker.go:7345 +0x52
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkExpression(...)
        github.com/microsoft/typescript-go/internal/checker/checker.go:7338
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkNonNullExpression(0xc0005a1908, 0xc00014e170)
        github.com/microsoft/typescript-go/internal/checker/checker.go:7190 +0x1f
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkPropertyAccessExpression(0xc0005a1908, 0xc000152108, 0x0, 0x0)
        github.com/microsoft/typescript-go/internal/checker/checker.go:10923 +0x48
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkExpressionWorker(0xc0005a1908, 0xc000152108, 0x1520b0?)
        github.com/microsoft/typescript-go/internal/checker/checker.go:7548 +0x2f7
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkExpressionEx(0xc0005a1908, 0xc000152108, 0x0)
        github.com/microsoft/typescript-go/internal/checker/checker.go:7345 +0x52
github.com/microsoft/typescript-go/internal/checker.(*Checker).checkExpression(...)
        github.com/microsoft/typescript-go/internal/checker/checker.go:7338
github.com/microsoft/typescript-go/internal/checker.(*Checker).resolveUntypedCall(0xc0005a1908, 0xc000152058?)
        github.com/microsoft/typescript-go/internal/checker/checker.go:9767 +0x1b2
github.com/microsoft/typescript-go/internal/checker.(*Checker).resolveErrorCall(...)
        github.com/microsoft/typescript-go/internal/checker/checker.go:9774
github.com/microsoft/typescript-go/internal/checker.(*Checker).resolveCallExpression(0xc0005a1908, 0xc000152058, 0x0, 0x0)
        github.com/microsoft/typescript-go/internal/checker/checker.go:8362 +0x235
github.com/microsoft/typescript-go/internal/checker.(*Checker).resolveSignature(0xc0005a2060?, 0xef7180?, 0xc000152058?, 0x826d2e?)
        github.com/microsoft/typescript-go/internal/checker/checker.go:8303 +0x46
github.com/microsoft/typescript-go/internal/checker.(...

</details>

- Fixes microsoft/typescript-go#2121

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/microsoft/typescript-go/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix panic on type predicate parameter mismatch Fix panic on negative parameterIndex in type predicate flow analysis Nov 18, 2025
Copilot AI requested a review from RyanCavanaugh November 18, 2025 22:55
Copilot finished work on behalf of RyanCavanaugh November 18, 2025 22:55
Copy link
Member

@RyanCavanaugh RyanCavanaugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot you have two code changes but only one test case. Add a second test that hits both cases (or prove to me that both changes were needed)

Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 18, 2025

@copilot you have two code changes but only one test case. Add a second test that hits both cases (or prove to me that both changes were needed)

Added second test case assertsPredicateParameterMismatch.ts that hits the second code path in isReachableFlowNodeWorker. Both fixes are needed:

  1. First fix (line 2408): Handles regular type predicates (value is Type) - tested by typePredicateParameterMismatch.ts
  2. Second fix (line 2499): Handles assertion predicates (asserts condition) with unreachable code detection - tested by assertsPredicateParameterMismatch.ts

I verified by temporarily removing the second fix - it causes a panic on the second test case. Both test cases now converted to testdata files in commit cc15e4a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • centralus-2.in.applicationinsights.azure.com
    • Triggering command: go build -tags=noembed,release -o ./built/local/ ./cmd/tsgo (dns block)
    • Triggering command: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.6.0 custom (dns block)
    • Triggering command: go build -ldflags -s -w -X &#39;main.version=v2.6.0-custom-gcl-94jqzDflNoq1ZZ2lGqIGB8QB8Hs6XG3sDaDhRRIDkMc&#39; -X &#39;main.date=2025-11-18 23:37:54.479336393 &#43;0000 UTC&#39; -o custom-gcl ./cmd/golangci-lint (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from RyanCavanaugh November 18, 2025 23:40
Copilot finished work on behalf of RyanCavanaugh November 18, 2025 23:40
@RyanCavanaugh RyanCavanaugh marked this pull request as ready for review November 19, 2025 03:06
Copilot AI review requested due to automatic review settings November 19, 2025 03:06
Copilot finished reviewing on behalf of RyanCavanaugh November 19, 2025 03:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a panic that occurred when a type predicate references a non-existent parameter name. When core.FindIndex cannot find a matching parameter, it returns -1, which was then used to index into arrays without bounds checking, causing a panic.

Key changes:

  • Added parameterIndex >= 0 check in getTypePredicateArgument before array access
  • Added parameterIndex >= 0 check in isReachableFlowNodeWorker for assertion predicates
  • Added comprehensive test coverage for both regular and assertion predicate scenarios

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
internal/checker/flow.go Added bounds checking for parameterIndex in two functions to prevent panic when type predicate parameter names don't match actual function parameters
testdata/tests/cases/compiler/typePredicateParameterMismatch.ts Test case for regular type predicate with mismatched parameter name
testdata/tests/cases/compiler/assertsPredicateParameterMismatch.ts Test case for assertion predicate with mismatched parameter name
testdata/baselines/reference/compiler/typePredicateParameterMismatch.* Expected baseline outputs (types, symbols, errors) for regular type predicate test
testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.* Expected baseline outputs (types, symbols, errors) for assertion predicate test

@RyanCavanaugh
Copy link
Member

Fixes #2121

Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, these are both cases where the old code relied on out of bounds accesses returning undefined

Yay JS

@jakebailey jakebailey added this pull request to the merge queue Nov 19, 2025
Merged via the queue into main with commit b33ab79 Nov 19, 2025
29 checks passed
@jakebailey jakebailey deleted the copilot/fix-type-predicate-mismatch branch November 19, 2025 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic: index out of range [-1] on type predicate parameter mismatch

3 participants