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 }
Description
When
"options": { "typeAware": true }is enabled,typescript/no-unnecessary-type-assertionincorrectly 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
querySelectoroverloads. 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 lintrepro.ts:
.oxlintrc.json:
{ "options": { "typeAware": true }, "rules": { "typescript/no-unnecessary-type-assertion": "error" } }Expected Behavior
load()without an explicit type argument should returnPromise<unknown>(genericTdefaults tounknown). Assertingawait load()toRecord<string, unknown>is a legitimate narrowing and should not be flagged.Without the assertion,
...actualcausesTS2698: Spread types may only be created from object types.Actual Behavior
Root Cause
Same as #20656 — oxlint resolves the generic type parameter
Tfrom theasassertion target (Record<string, unknown>) instead of using the declared default (unknown). It then concludes the expression "already has" the asserted type.Impact
--fixsilently removes necessary type assertions, causing TypeScript compilation errors (TS2698)const actual = (await importOriginal()) as Record<string, unknown>— vitest'simportOriginalhas the signature<T extends M = M>() => Promise<T>whereM = unknownEnvironment
"options": { "typeAware": true }