Skip to content

linter: no-unnecessary-type-assertion false positive on generic functions with default type parameters #20682

@EurFelux

Description

@EurFelux

Description

When "options": { "typeAware": true } is enabled, typescript/no-unnecessary-type-assertion incorrectly flags type assertions on generic function calls with default type parameters as unnecessary.

This is a follow-up to #20656, which fixed the same class of bug for querySelector overloads. The underlying issue persists for simpler generic functions with default type parameters.

Minimal Reproduction

Repo: https://github.com/EurFelux/oxlint-type-assertion-bug/tree/main/cases/generic-default-param

git clone https://github.com/EurFelux/oxlint-type-assertion-bug
cd oxlint-type-assertion-bug/cases/generic-default-param
pnpm install
pnpm run lint

repro.ts:

declare function load<T = unknown>(): Promise<T>

export async function main() {
  const actual = (await load()) as Record<string, unknown>
  return { ...actual }
}

.oxlintrc.json:

{
  "options": { "typeAware": true },
  "rules": { "typescript/no-unnecessary-type-assertion": "error" }
}

Expected Behavior

load() without an explicit type argument should return Promise<unknown> (generic T defaults to unknown). Asserting await load() to Record<string, unknown> is a legitimate narrowing and should not be flagged.

Without the assertion, ...actual causes TS2698: Spread types may only be created from object types.

Actual Behavior

  x typescript-eslint(no-unnecessary-type-assertion): This assertion is unnecessary since it does not change the type of the expression.
    ,-[repro.ts:4:33]
  3 | export async function main() {
  4 |   const actual = (await load()) as Record<string, unknown>
    :                   ^^^^^^|^^^^^  ^^^^^^^^^^^^^|^^^^^^^^^^^^^
    :                         |                    `-- Casting it to 'Record<string, unknown>' is unnecessary
    :                         `-- This expression already has the type 'Record<string, unknown>'
    `----

Root Cause

Same as #20656 — oxlint resolves the generic type parameter T from the as assertion target (Record<string, unknown>) instead of using the declared default (unknown). It then concludes the expression "already has" the asserted type.

Impact

  • Using --fix silently removes necessary type assertions, causing TypeScript compilation errors (TS2698)
  • This pattern is very common in vitest mocks: const actual = (await importOriginal()) as Record<string, unknown> — vitest's importOriginal has the signature <T extends M = M>() => Promise<T> where M = unknown
  • Affects any generic function with a default type parameter

Environment

  • oxlint: 1.56.0
  • oxlint-tsgolint: 0.17.2
  • TypeScript: 5.8.3
  • Requires: "options": { "typeAware": true }

Metadata

Metadata

Assignees

Labels

Type

Priority

None yet

Effort

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions