-
Notifications
You must be signed in to change notification settings - Fork 134
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
Don't declare private fields in user facing definition files #223
Comments
@spion There are a couple topics at play here: The compiler's check: In my view, the error reported by the TypeScript compiler is correct and useful. When two different objects implement the same Yarn's installation strategy: In your example, Yarn is choosing versions differently from how NPM and PNPM do it. (There is a very long debate about whether this is a good idea or not, but we can't solve that here.) In any case, in your example Yarn has produced an installation that is not safe. The practical way to fix it is by adjusting version ranges in I don't see how we could fix this in the TSDoc project (unless I'm missing something?). Where is the |
As per semver, modules are not supposed to break their contract in a minor or patch release. However, adding a private field to the contract automatically breaks that compatibility for every single subsequent version released (major, minor or patch) with no way to opt-out. The resolution strategy in Yarn is safe. It enforces the maximum version possible since that's the version least likely to contain vulnerabilities and to be fully patched and up to date. There are several ways to solve this:
Regarding the safety lesson:
Regardless of debates around package managers, libraries should generally strive to accommodate many different configurations, rather than pick a subset of configurations that they're compatible with. "Works with my setup" isn't an appropriate quality bar for professional software. 😀 (Sorry for the counter-jab, I found it too hard to resist!) |
Your usage of the word "contract" here seems somewhat loose. Consider this code: import { Service, Manager } from "the-library";
const manager: Manager = new Manager();
cons service: Service = new Service(manager);
manager.start(service); Generally SemVer means that a consumer can develop the above application using Whereas if I understand correctly, your use case is more like this: import { Service } from "the-library@1.1.0";
import { Manager } from "the-library@1.2.3";
const manager: Manager = new Manager();
cons service: Service = new Service(manager);
manager.start(service); In other words, we are expecting two instances of
🤔I wonder how easy this would be. It seems like a lot of trouble to enable a use case that we didn't really intend to support. I'm open-minded about it, though.
This can happen after TSDoc reaches version Currently TSDoc does try to follow SemVer (using NPM's
They might have an option for that. Also ECMAScript has a new |
I'm guessing you meant to give an example with two connected instances of the same class, one taking the other as an argument - at least that's the easiest example I can think of. Unfortunately, the reality in JS (due to how npm and package managers that are alternative implementations of the same philosophy work) is that we have to accept that any user-passed inputs could come from version-incompatible libraries, even those that are instances of the same class. We've had this actually happen with Bluebird for example - bluebird had a fast path for "thenable" conversion to promise when the thenable was coming from Bluebird, however it used "instanceof" checks which would fail with different versions installed in different places in the filesystem. Bluebird switched to property based recognition and detection to avoid that particular issue. In TypeScript I'm guessing the right solution is to make the private field check structural (unless inheritance is in play) and rely on deliberate modifications for private fields and methods to signal incompatibility at compile-time |
@spion Some questions:
|
(1): Its actually a bit worse than that. If library A is using libraries, B, C, tslib and D, together; and B and C use one version of tslib (say 1.0.1); while A and D depend on another (say 1.0.2), it's possible for yarn to choose the main tslib (1.0.2) as a primary and the one used by B and C as copies in the filesystem (even though they are the same 1.0.1 version). In this scenario, even if you're not passing objects from the top-level tslib to B and C, there would still be compile-time errors if B and C interact - while their copies are exactly the same version, they have to be placed in different places in the filesystem and would, therefore, be considered different and incompatible by the compiler. |
When tsdoc is used from multiple libraries each having a slightly different version, private fields cause type incompatibilites. For example:
This is due to slightly different versions requested by the libraries, as shown in yarn.lock:
Yarn always prefers the newest version possible for each module - however, private fields make even "revision" versions incompatible.
The text was updated successfully, but these errors were encountered: