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

Introduce strictCustomConditions option for custom exports / imports conditions with no fallback to other conditions if the file does not exist #61363

Open
6 tasks done
aweebit opened this issue Mar 6, 2025 · 4 comments

Comments

@aweebit
Copy link

aweebit commented Mar 6, 2025

πŸ” Search Terms

custom conditions, package.json exports / imports, customConditions, TSConfig

βœ… Viability Checklist

⭐ Suggestion

TypeScript does not report an error if a custom condition specified in customConditions is encountered, but the file it resolves to does not exist. Instead, it keeps trying to resolve the requested module using other conditions such as types or default (β†’ Demo).

By contrast, Node.js stops trying as soon as the first relevant condition is encountered and throws a runtime error if the file it resolves to does not exist (β†’ Demo).

The motivation behind this fallback mechanism of TypeScript is probably to avoid problems in cases where my package happens to use the same custom condition name as one of its third-party dependencies. For example, both my package and its dependency could use the source condition to resolve to the source .ts files, but since those are normally not published, trying to import from this dependency would result in an error if the fallback mechanism were not in place.

If my idea is right and this is indeed the motivation for the behavior, well, I am not very convinced. I think there is only one right solution to problems that could arise due to using the same custom condition names as third-party dependencies, and that is to make sure you don't! One common pattern to avoid such conflicts are scoped condition names suggested by @colinhacks, the creator of Zod, in Live types in a TypeScript monorepo.

So as not to introduce a breaking change to the behavior of customConditions, I suggest introducing a new TSConfig option for specifying custom conditions for which the fallback mechanism would not be used. I think strictCustomConditions would be a good name for it.

I still haven't explained how it could be useful though, and that's what I am going to do now.

πŸ“ƒ Motivating Example

What motivated me to post this feature request was the realization there was no good way to use project references in monorepos employing workspaces I came to during a discussion I started with this comment in #40431.

The issue is shown very well in this demo I adapted from one @robbiespeed posted in that thread. There, @ts-ref/source custom conditions are used for source .ts files. If you run pnpm build, then rename packages/a/foo.ts, and then run pnpm build again, you will not see an error despite the foo.ts file that we mean to import from bar.ts no longer being available.

In general, if you delete, move or rename a source .ts file that has already been compiled with "declaration": true (which is how all referenced projects are compiled because they are required to be composite), TypeScript will not report an error when trying to import from it unless you also manually delete the corresponding declaration file. The reason is exactly the fallback mechanism: instead of stopping and reporting an error when the source file doesn't exist, TypeScript continues to inspect the remaining exports conditions and ends up resolving with the types condition that provides the declaration file.

This behavior can cause developers a lot of confusion, e.g. because imports from files that shouldn't exist anymore are suggested in the editor. There have been a couple other proposals that could solve the Issue:

However, it doesn't look like either of these ideas is going to be implemented any time soon, which is exactly why I am opening this issue so as to propose an alternative solution and hopefully have more luck with it :)

πŸ’» Use Cases

  1. What do you want to use this for?
    β†’ For β€œlive types” in monorepos based on workspaces.
  2. What shortcomings exist with current approaches?
    β†’ The shortcomings of customConditions are described above.
  3. What workarounds are you using in the meantime?
    β†’ Not using workspaces whenever possible, otherwise using Google's Wireit tool to automatically delete declaration files for which the corresponding source files no longer exist (see comment).
@aweebit
Copy link
Author

aweebit commented Mar 6, 2025

On a completely unrelated side note, I would like to ask the TypeScript team to have a look at #61256 (comment). The issue was closed too quickly for me to write an answer, and now I think no one is going to see it unless I mention the issue again. I am convinced it should be reopened, and the comment explains why in great detail.

@Andarist
Copy link
Contributor

Andarist commented Mar 6, 2025

This could be seen as a duplicate of #50762

@aweebit
Copy link
Author

aweebit commented Mar 6, 2025

@Andarist it is closely related for sure. What is the current status of that issue? I see it mentioned as a deprecation candidate for TypeScript 6.0 in #54500, which is not too bad considering the version will probably be released this year already.

To help solve the issue with monorepos, I would suggest not only deprecating the fallback mechanism, but also introducing a boolean option to completely disable it. The option could be named noConditionFallback and should be made always true (and therefore also deprecated) in some major version release in the distant future.

One issue with it is that it could only be used if all the third-party dependencies of my package have already structured their exports correctly. Unfortunately, despite all your efforts back in 2023, there are still big libraries that haven't done the necessary restructuring (yargs/yargs#2327, then/is-promise#50, etc.), so introducing strictCustomConditions as a temporary measure still seems reasonable to me.

@aweebit
Copy link
Author

aweebit commented Mar 7, 2025

Since it turns out the fallback mechanism is actually a bug rather than a feature, here are two alternative suggestions:

  • Introduce a boolean option that would disable the fallback mechanism for custom conditions

    The name could be noCustomConditionFallback. The idea is very similar to noConditionFallback from the previous comment, the only difference is that the fallback mechanism would be disabled only for the conditions specified in customConditions. Enabling the option would solve the monorepo problem without breaking dependencies still having their exports in the wrong order since for those, the fallback mechanism would still apply.

  • Completely disable the fallback mechanism for custom conditions

    Technically, this would be a breaking change, but I don't think it should cause a lot of trouble. Again, everyone using customConditions even today should make sure the listed conditions don't clash with those from third-party dependencies.

    So the plan would be to deprecate the fallback mechanism for the standard conditions, but get rid of it completely for custom conditions as of version 6.0. I am a big fan of this idea.

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

2 participants