Repository: TypeScript
Issue: Design Meeting Notes, 10/1/2024
Labels: Design Notes

# The `internal` Modifier

https://github.com/microsoft/TypeScript/issues/5228

* We've discussed an `internal` modifier on-and-off since 2015.
* High 👍 count, but haven't pursued it.
* Today you can use `/** @internal */` JSDoc comments to mark things as internal, and `--stripInternal`.
* Previously, `--stripInternal` was not even publicly documented.
* Also, have to do hacks to make `.d.ts` bundling work with this.
* Would be nice to have something more formal.
    * Also, `internal` allows you to know whether something is getting overridden in a subtype.
* Idea:
    * `internal` stays in `.d.ts` output.
    * Project package or project boundary.
        * Only operates in the same compilation.
    * Not allowed to `import` an `export internal`.
    * At the `tsconfig.json` level, internal access might be granted and asserted in a few ways:
        * Maybe an option for specific packages

          ```json5
          {
              "compilerOptions": {
                  // Grant access to 'consuming-package':
                  "internalsVisibleTo": ["consuming-package"],

                  // Assert access to 'dependency-package':
                  "internalsVisibleFrom": ["dependency-package"],
              }
          }
          ```
        * Possibly just something on each reference

          ```json5
          {
              "references": [
                  { "path": "../package", "allowInternals": true }
              ]
          }
          ```
* What are the rules?
    * Can't mix (e.g. no `public` and `internal` in the same declaration).
* Why not JSDoc?
    * Relatively expensive for this.
* What should tools do with `internal`?
    * Should bundlers limit access to an `internal` member that's `export *`'d?
        * We'd sort of hope "no".
    * Does the declaration emitter have to worry about this now?
    * What happens when you run `keyof` on a type with `internal`?
        * Probably should do the same thing that `private` does.
        * But does that mean that `keyof` means something different depending on where it's written.
    * How does this work for overload resolution?
        * Do overloads get skipped?
        * Do they get related appropriately?
* The "someone subclassed this" issue is not something we've heard a ton of.
* Do most people use `.d.ts` bundlers that can do this post-processing?
    * No, mostly not.
* Some of our build tooling would be simpler if we had this.
* But doesn't it mean that we're just making other stuff more complex?
    * For example, `public` and `private` overloads can't be mixed today.
    * Well maybe we do need to explore that restriction.
* Also, some of the point of `--stripInternal` is to not make things publicly known and avoid documenting functions to prevent usage.
* Back to this question again - what do you do when you have a mapped type?
    * `internal` is inaccessible *depending* on where it's used.
    * `private` and `protected` has this, but it's not witnessable in the majority of code (which is outside of the class).
* Coming back to `keyof` - is this now a location-sensitive operation?
    * The idea that you will get different types based on instantiation location is not something we've done before.
        * Really don't like that.
* What do you want?
    * Scope visibility to peers.
    * Keep existence undocumented.
* Do we really want an ergonomic way for people to import something marked `internal`?
    * We don't have to do everything listed above.
* Can do this just on the module level.
    * Maybe even just that and the property level.
    * "Good enough" as `readonly`.
* Cautious but curious around a solution.
    * Not convinced that we would have a solution that we'd entirely be happy with.
    * Must preserve existing invariants around how type information appears.
* One opinion: sympathetic to a `package`-like modifier, but doesn't entirely solve the problem for our own usage for hiding entities in the resulting declaration file.