Skip to content

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

@aweebit

Description

@aweebit

πŸ” 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions