Skip to content

Bug: only-throw-error rule allow option with package specifier never matches types from npm packages #663

@ItaiYosephi

Description

@ItaiYosephi

Summary

The allow option for the typescript/only-throw-error rule accepts package specifiers (e.g. { "from": "package", "name": "ErrorLike", "package": "@apollo/client" }), but types from real npm packages never match. Only types declared inside ambient declare module "..." blocks can match. This makes the option ineffective for common use cases like allowing Apollo Client’s ErrorLike to be thrown.

Environment

  • Repository: oxc-project/tsgolint
  • Rule: typescript/only-throw-error
  • Config: .oxlintrc.json with oxlint --type-aware
  • Example dependency: @apollo/client (or any npm package that exports types via normal .d.ts files)

Steps to reproduce

  1. Install oxlint and oxlint-tsgolint, enable type-aware linting.
  2. In .oxlintrc.json, configure:
{
  "rules": {
    "typescript/only-throw-error": [
      "error",
      {
        "allow": [
          {
            "from": "package",
            "name": "ErrorLike",
            "package": "@apollo/client"
          }
        ]
      }
    ]
  }
}
  1. In a TS file, use Apollo’s useLazyQuery and throw its error:
import { useLazyQuery } from '@apollo/client/react';

const [query] = useLazyQuery(SomeDocument);

async function run() {
  const { data, error } = await query({});
  if (error) {
    throw error;  // ← typescript-eslint(only-throw-error): Expected an error object to be thrown.
  }
}
  1. Run oxlint --type-aware.

Expected: No report for throw error, because error is typed as ErrorLike from @apollo/client and that type is in allow.

Actual: The rule reports: Expected an error object to be thrown. The package specifier is ignored.

Root cause

In internal/utils/type_matches_specifier.go:

  1. Package specifiers are handled by typeDeclaredInPackageDeclarationFile, which is true only if either:

    • typeDeclaredInDeclareModule(packageName, declarations) is true, or
    • typeDeclaredInDeclarationFile(packageName, declarationFiles, program) is true.
  2. typeDeclaredInDeclarationFile (lines 219–241) is effectively unimplemented:

    • The logic that would map declaration files to package names is commented out with a TODO(port) (“there is no sourceFileToPackageName anymore”).
    • The function always returns false.
  3. typeDeclaredInDeclareModule (lines 210–217) only returns true when the type is declared inside an ambient declare module "package-name" { ... } block. Types that come from normal npm package .d.ts files (e.g. export interface ErrorLike in @apollo/client/core/types.d.ts) are not inside such a block, so they never match.

Result: for types from real npm packages, the package specifier path is never taken, so the allow list does not work for them.

Suggested fix

Implement (or re-enable) the mapping from declaration files to package names so that typeDeclaredInDeclarationFile can return true when the type’s declaration file belongs to the requested package (e.g. when the file is under node_modules/@apollo/client/ for package: "@apollo/client"). Alternatively, document that package specifiers are only supported for declare module-style declarations until this is implemented.

References

  • Rule implementation: internal/rules/only_throw_error/only_throw_error.go (uses utils.TypeMatchesSomeSpecifier(t, opts.Allow, ctx.Program)).
  • Specifier matching: internal/utils/type_matches_specifier.go (typeMatchesSpecifier, typeDeclaredInPackageDeclarationFile, typeDeclaredInDeclarationFile, typeDeclaredInDeclareModule).
  • Related discussion: Issue #425 (proposal to remove allow; this issue shows it is currently broken for package specifiers rather than merely “rarely used”).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions