Skip to content

local types are considered exported in .d.ts files unlike local types in .ts filesΒ #57764

Closed as not planned
@eps1lon

Description

@eps1lon

πŸ”Ž Search Terms

ts2460 export type

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried (down to 2.0), and I reviewed the FAQ for entries about ?

⏯ Playground Link

No response

πŸ’» Code

Full repro: https://github.com/eps1lon/ts-module-auto-export

index.tsx

import type { Private as DeclarationPrivate } from "./declaration";
import type { Private as LibraryPrivate } from "./library";

export const foo: DeclarationPrivate = { foo: "bar" };
export const bar: LibraryPrivate = { foo: "bar" };

declaration.d.ts

/**
 * @internal
 */
type Private = {
  foo: string
}

export type Public = Private | null

library.ts

/**
 * @internal
 */
type Private = {
  foo: string
}

export type Public = Private | null

πŸ™ Actual behavior

Import of local types is allowed in .d.ts files. Only Module '"./library"' declares 'Private' locally, but it is not exported.ts(2459) is raised

πŸ™‚ Expected behavior

Local types are not exposed from .d.ts files unless they have an export modifier. We should get Module '"./declaration"' declares 'Private' locally, but it is not exported.ts(2459) as well.

The other issue is that TypeScript removes the local types from the declaration for library.ts instead of keeping it as a local type.

This is a problem for libraries since every type is automatically part of the public API and we can't easily remove implementation details.

If TypeScript would hide local types, it would also need to make sure that emitted declarations don't reference those types.

The real-world issue is with React types. If TypeScript has to infer the type it may decide to either reference React.ReactNode explicitly (good) or inline all its members (bad) e.g.

class Component extends React.Component<{ children?: React.ReactNode }> {
  render() {
    if (Math.random()) {
      return <div />
    }
    return this.props.children
  }
}

will be emitted as

export declare class Component extends React.Component<{ children?: React.ReactNode }> {
    render(): string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
}

Additional information about the issue

Original issue: vercel/next.js#63185

Maybe related to

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions