Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusing classes with private fields across entrypoints/submodules causes not working type definitions to be generated #90

Open
AKuederle opened this issue Oct 8, 2024 · 0 comments

Comments

@AKuederle
Copy link

I ran into an issue with classes shared across entry-points with private fields. I feel like I am missing the proper terminology here, so I hope I am able to explain this properly.

The issue

Classes with private attributes are considered different types when exported from multiple different entrypoints.

Setup and Reproduction

First of all, a reproduction can be found in this repo: https://github.com/AKuederle/dts-buddy-repro
This should explain the setup required better than words can.

But here the a short explanation:

I have a package with to entrypoints:

    ".": {
      "types": "./types/index.d.ts",
      "import": "./src/index.js"
    },
    "./format": {
      "types": "./types/index.d.ts",
      "import": "./src/format.js"
    }

Both entrypoints contain functions/methods that make use of the same class. In the reproduction this is the Person class from ./src/person.js. This class is directly exported via the main entrypoint in ./src/index.js and used as import type in a function exported from ./src/format.js

In the type file created by dts-buddy, this shared class is repeated in the type defintion of both entrypoints as follows:

	class Person {
		/**
		 * Create a person object.
		 * @param name - The name of the person.
		 * @param age - The age of the person.
		 * @param email - The email address of the person.
		 */
		constructor(name: string, age: number, email: string);
		name: string;
		age: number;
		email: string;
		
		get id(): string;
		#private;
	}

Importantly, because the class has a private field #id, dts-buddy includes a #private; at the end. Typescript considers this field to have the type any.
However, because the definition is repeated in the types file and one of the fields are any, Typescript considers these two exported classes to be different.
When attempting to use functions from both entrypoints together a type error is raised:

import { Person } from 'exampleLib';
import { formatPerson } from 'exampleLib/format';

const person = new Person('John Doe', 34, "[email protected]");
// This does not work, because the Person class has a private field.
// This triggers dts-buddy to include a `#private` field in the generated types.
console.log(formatPerson(person));
                         ^^^^^^^ 
Argument of type 'import("exampleLib").Person' is not assignable to parameter of type 'Person'.
  Property '#private' in type 'Person' refers to a different member that cannot be accessed from within type 'Person'.ts(2345)

If the Person class has no private field (tested with the PersonNoPrivate class in the reproduction), Typescript considers the two type exports the same and no error is raised.

Expectation

As I am using the same class, I would assume that Typescript considers them the same independent on how they are imported.

Actually, I was surprised, that dts-buddy repeated the type definition across both modules. My naive assumption was, that the exported type module for the format entrypoint would be this:

declare module 'exampleLib/format' {
	import { Person } from 'exampleLib';

	export function formatPerson(person: Person): string;

	export {};
}

There is a lot here, I don't understand and in particular don't understand the impact of exporting types one way or the other. So, I hope you can shed some light on what is going on here and if this is actually a bug or just me misunderstanding, how types should be re-exported.

Thanks in advance! And thanks for making jsdoc types a viable alternative to writing TS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant