Skip to main content

no-internal

Disallow usage of symbols tagged with @internal.

Targeted pattern scopeโ€‹

โš ๏ธ This rule requires type information to run. Configure type-aware linting (parserOptions.project or projectService) before enabling it.

This rule inspects identifier usages and resolves each identifier to a TypeScript symbol.

If the resolved symbol has one or more @internal JSDoc tags, usage is reported (unless ignored via configuration).

What this rule reportsโ€‹

APIs marked as @internal are implementation details that can change without notice. Referencing them from external call sites couples your code to unstable contracts and makes upgrades risky.

This rule reports identifier usages whose resolved TypeScript symbol includes one or more @internal tags.

Why this rule existsโ€‹

It prevents accidental dependency on unstable internals that are intentionally outside a package's supported API surface.

โŒ Incorrectโ€‹

/** @internal */
interface InternalType {
readonly value: number;
}

const item: InternalType = { value: 42 };
/** @internal Internal function details */
declare function internalFunction(): void;

internalFunction();

โœ… Correctโ€‹

interface PublicType {
readonly value: number;
}

const item: PublicType = { value: 42 };
/** @internal */
interface InternalType {
readonly value: number;
}

// Declaration is allowed. Only usage is reported.

Behavior and migration notesโ€‹

This rule reports only and does not provide an autofix.

It ignores declaration identifiers and import/export specifier identifiers, and focuses on usage sites resolved through TypeScript symbol information.

Optionsโ€‹

type Options = [
{
ignored?: Record<string, "name" | "path">;
}?,
];

Default:

[{}];

Use ignored to suppress some internal symbols by regular-expression pattern:

  • "name": Match against the symbol name.
  • "path": Match against the symbol's fully-qualified declaration path.

If an ignored key is not a valid regular expression, the rule reports a configuration error (invalidIgnorePattern) instead of silently ignoring it.

Example:

import etcMisc from "eslint-plugin-etc-misc";

export default [
{
plugins: { "etc-misc": etcMisc },
rules: {
"etc-misc/no-internal": [
"error",
{
ignored: {
"^ExperimentalInternalType$": "name",
"modules/internal": "path",
},
},
],
},
},
];

Additional examplesโ€‹

/** @internal Internal helper */
export declare const __internalThing: string;

const value = __internalThing;
// โŒ reported

// config: { ignored: { "^__internalThing$": "name" } }
const ignoredValue = __internalThing;
// โœ… ignored by configuration

ESLint flat config exampleโ€‹

import etcMisc from "eslint-plugin-etc-misc";

export default [
{
plugins: { "etc-misc": etcMisc },
rules: {
"etc-misc/no-internal": "error",
},
},
];

When not to use itโ€‹

Disable this rule if your project intentionally consumes internal contracts and accepts the maintenance risk from those unstable dependencies.

Package documentationโ€‹

Rule catalog ID: R028

Further readingโ€‹

Adoption resourcesโ€‹

  • Start at warning level in CI, then move to error after cleanup.
  • Use focused codemods/autofix batches per package or directory.