Skip to content

TS4053 happens when public class method adopts imported type from library #2911

@hkleungai

Description

@hkleungai

Steps to reproduce

  1. Start a plain new project
    a. "@typescript/native-preview": "^7.0.0-dev.20260225.1"
    b. "typescript": "6.0.0-beta"
  2. Run npx tsgo --init to get the default tsconfig.
  3. Add a src/lib.ts & src/main.ts. Run npx tsc --noEmit & npx tsgo --noEmit to see the difference.
// lib.ts
interface Base { }

export type ExportBase = Base;

// main.ts
import type { ExportBase } from './lib.js'

interface Clonable<T> {
    clone(): Clonable<T>
}

class Internal_Class {
    with_key_ExportBase_param(param: { key: ExportBase }) {
        if (Math.random() > 0.5) {
            param;
        } else {
            return param;
        }
    };

    with_clonable_key_ExportBase_param(param: Clonable<{ key: ExportBase }>) {
        if (Math.random() > 0.5) {
            param.clone();
        } else {
            return param.clone();
        }
    };

    with_ExportBase_param(param: ExportBase) {
        if (Math.random() > 0.5) {
            param;
        } else {
            return param;
        }
    };

    with_clonable_ExportBase_param(param: Clonable<ExportBase>) {
        if (Math.random() > 0.5) {
            param.clone();
        } else {
            return param.clone();
        }
    };
}

export default class Exported_Class {
    with_key_ExportBase_param(param: { key: ExportBase }) {
        if (Math.random() > 0.5) {
            param;
        } else {
            return param;
        }
    };

    with_clonable_key_ExportBase_param(param: Clonable<{ key: ExportBase }>) {
        if (Math.random() > 0.5) {
            param.clone();
        } else {
            return param.clone();
        }
    };

    with_ExportBase_param(param: ExportBase) {
        if (Math.random() > 0.5) {
            param;
        } else {
            return param;
        }
    };

    with_clonable_ExportBase_param(param: Clonable<ExportBase>) {
        if (Math.random() > 0.5) {
            param.clone();
        } else {
            return param.clone();
        }
    };
}

Behavior with typescript@6.0

src/main.ts:59:5 - error TS4053: Return type of public method from exported class has or is using name 'Base' from external module "project-directory/src/lib" but cannot be named.

59     with_ExportBase_param(param: ExportBase) {
       ~~~~~~~~~~~~~~~~~~~~~

src/main.ts:67:5 - error TS4053: Return type of public method from exported class has or is using name 'Base' from external module "project-directory/src/lib" but cannot be named.

67     with_clonable_ExportBase_param(param: Clonable<ExportBase>) {
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Found 2 errors in the same file, starting at: src/main.ts:59

Behavior with tsgo

src/main.ts:43:5 - error TS4053: Return type of public method from exported class has or is using name 'Base' from external module "project-directory/src/lib" but cannot be named.

43     with_key_ExportBase_param(param: { key: ExportBase }) {
       ~~~~~~~~~~~~~~~~~~~~~~~~~

src/main.ts:51:5 - error TS4053: Return type of public method from exported class has or is using name 'Base' from external module "project-directory/src/lib" but cannot be named.

51     with_clonable_key_ExportBase_param(param: Clonable<{ key: ExportBase }>) {
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/main.ts:59:5 - error TS4053: Return type of public method from exported class has or is using name 'Base' from external module "project-directory/src/lib" but cannot be named.

59     with_ExportBase_param(param: ExportBase) {
       ~~~~~~~~~~~~~~~~~~~~~

src/main.ts:67:5 - error TS4053: Return type of public method from exported class has or is using name 'Base' from external module "project-directory/src/lib" but cannot be named.

67     with_clonable_ExportBase_param(param: Clonable<ExportBase>) {
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Found 4 errors in the same file, starting at: src/main.ts:43

I did find myself a few workarounds.

  1. Add back or remove all return on all the branches in method(); but this could be impractical for a large codebase.
  2. Export Base together with ExportBase; but it requires (hacky) package patching when it comes to 3rd party dependencies..

Also note that the error goes away if ExportBase is NOT purely an alias of Base. In other words, the below typing will actually pass tsc & tsgo typechecking.

- export type ExportBase = Base;
+ export type ExportBase = Base & { extra: number; };

Metadata

Metadata

Assignees

No one assigned

    Labels

    Non-regressionBehavior that is bad in Corsa but also bad in Strada

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions