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

Module documentation tracking issue #52593

Closed
andrewbranch opened this issue Feb 3, 2023 · 20 comments · Fixed by microsoft/TypeScript-Website#2946
Closed

Module documentation tracking issue #52593

andrewbranch opened this issue Feb 3, 2023 · 20 comments · Fixed by microsoft/TypeScript-Website#2946
Assignees
Labels
Docs The issue relates to how you learn TypeScript

Comments

@andrewbranch
Copy link
Member

andrewbranch commented Feb 3, 2023

I’m working on new documentation for modules in TypeScript. I’ll use this issue to track progress. Related:

@andrewbranch andrewbranch added the Docs The issue relates to how you learn TypeScript label Feb 3, 2023
@andrewbranch andrewbranch self-assigned this Feb 3, 2023
@andrewbranch
Copy link
Member Author

Proposed, very ambitious outline, subject to change:

  1. Introduction
    1. Who is this for?
  2. Theory
    1. Scripts and modules in JavaScript
    2. What is TypeScript’s job concerning modules?
    3. Module emit
      1. Input syntax is (somewhat) decoupled from output
      2. Note: verbatimModuleSyntax
      3. Note: top-level await
      4. Module format interop
      5. Module specifiers are not transformed
    4. Module resolution
      1. Module resolution is host-defined
      2. Who is the host?
      3. TypeScript “mirrors” the host resolver
        1. Module specifiers reference the host’s source files
        2. Declaration files are substituted for JS files
        3. Note: non-JS files supported with allowArbitraryExtensions
      4. Common resolution features
        1. Omittable extensions and directory index files
        2. node_modules and package.json
          1. @types
          2. package.json "exports"
      5. Node’s dual-algorithm system
        1. CJS/ESM detection
          1. File extensions
          2. package.json "type"
        2. ESM algorithm restrictions
        3. package.json "exports" conditions
        4. Importing ESM from CJS requires dynamic import
      6. Bundlers and other Node-like non-Node hosts
        1. noEmit and allowImportingTsExtensions
        2. Customization options
    5. Caveats / limitations?
      1. Why module specifiers are not transformed
      2. Why dual-emit is not supported and sometimes impossible
      3. Future work? Browser, import maps, URLs?
  3. Guides
    1. Choosing compiler options
    2. Troubleshooting dependency issues
    3. Publishing a library
  4. Reference
    1. Module syntax
      1. ESM (link elsewhere?)
      2. CJS in JavaScript
      3. CJS in TypeScript
      4. Type-only imports and exports
      5. Ambient module declarations & augmentations?
    2. Module emit and checking
      1. module
        1. commonjs
        2. es2015+
        3. node16/nodenext
      2. CJS/ESM interop details
      3. The default problem
      4. __esModule marker
      5. allowSyntheticDefaultExports
      6. esModuleInterop
      7. verbatimModuleSyntax
    3. Module resolution
      1. moduleResolution
        1. node16/nodenext
        2. bundler
      2. allowJs and maxNodeModuleJsDepth
      3. package.json
        1. "main", "types"
        2. "typesVersions"
        3. "exports"
        4. "type"
        5. "imports"
      4. Customization compiler options
        1. paths
        2. baseUrl
        3. resolvePackageJsonImports
        4. resolvePackageJsonExports
        5. customConditions
        6. allowImportingTsExtensions
        7. resolveJsonModule
        8. rootDirs
        9. typeRoots
        10. moduleSuffixes

@fatcerberus
Copy link

Who is this for?

Approximately everybody 😄

@RyanCavanaugh
Copy link
Member

Another section we'll need is effectively "What is the right thing to put on DefinitelyTyped?"

@DanielRosenwasser
Copy link
Member

What module settings should my new project use?

@andrewbranch
Copy link
Member Author

@DanielRosenwasser please refer to section 3.i

@fatcerberus
Copy link

fatcerberus commented Feb 4, 2023

What module settings should my new project use?

All of them

@jmrossy
Copy link

jmrossy commented Feb 5, 2023

@andrewbranch Two more fields worth covering in 4.iii.c:
module and browser

@robpalme
Copy link

robpalme commented Feb 6, 2023

I'm not sure which section this would go in, but there is a semi-obscure question/choice I'd like to get an authoritative answer on, so I'm kinda hijacking this issue as a place to resolve it.

Question

  • Is it legitimate for a project to store hand-written utility types in a local *.d.ts source file instead of using a *.ts source file?

It's common to have local files in which you might store utility types. Maybe you put them in a directory called util-types/. So which file extension type should you put in this directory?

  • A full-fat *.ts file would work, but at best will result in an empty *.js file being emitted, and at worst might deviate from programmer intent over time if someone accidentally inserts value-land code.
  • Whereas a *.d.ts enforces this intent and guarantees to have zero JS emit - it only is needed during checking.

Here's some example usage in a multi-file playground.

// @filename: index.ts
import type { Point } from "./util-types/only-types"
export const location: Point = {
    x: 0,
    y: 0,
}
// @filename: util-types/only-types.d.ts
export interface Point {
    x: number,
    y: number,
}

Potential Answers

I'm looking for a steer on whether the use of *.d.ts files for storing hand-written utility types is:

  1. Encouraged: because this style has active benefits
  2. Supported: but there's no value judgement on which style you should pick
  3. Discouraged: this style is supported and not going away, but may confuse people
  4. Harmful: it may seem to work but will hurt you later or might be deprecated

The answer will help inform whether we should internally force the use of *.ts files for this purpose by banning hand-written *.d.ts files.

@andrewbranch
Copy link
Member Author

The answer to this question is unsatisfying to me. I would say it’s discouraged or even harmful (though it won’t be “deprecated”). At best, it’s a technique with serious pitfalls that can be leveraged by people who understand them enough to set up additional tooling and safeguards to make it viable. Because .d.ts files only occur “naturally” as a pair with a .js file, together as outputs of a .ts file, a .d.ts file always implies the existence of a .js file. So the potential harm is readily apparent: if you hand-author only-types.d.ts and then write import {} from "./only-types.js", this resolves and is legal in all settings, but in verbatimModuleSyntax, the import will be preserved and crash at runtime. While TypeScript has type-only imports and exports, it lacks the analogous concept of a type-only module, one which exists for type information purposes but is known to not exist at runtime, though I’ve casually suggested multiple times that such a concept could be useful.

But the main reason I personally avoid this is just because it doesn’t copy into outDir. For my purposes, I’d rather just eat the cost of the empty JS file (which also protects you from crashes should you accidentally import it at runtime).

@benasher44
Copy link

Never been so hyped for documentation

@RyanCavanaugh
Copy link
Member

How to make dual-mode packages? #53045

@benasher44
Copy link

benasher44 commented Mar 23, 2023

I'm not sure if this is too much, but guidance on how to navigate incompatible packages when migrating a TS project from CJS to ESM would be helpful. I can file tickets on projects to fix their package.json and build processes all day, but ultimately it can be very hard to know precisely what is broken, what to tell library authors, and what patch I need to apply to the package to hold me over in nodenext + CJS mode to prepare to go to nodenext + ESM mode.

@andrewbranch
Copy link
Member Author

There’s enough done now to start sharing some in-progress content for those interested. I’ll keep this gist up to date as I finish significant chunks. Feel free to leave feedback in the gist comments.

@andrewbranch
Copy link
Member Author

Quick update: this is getting fairly close to being done and anticipate getting a PR up to put this on the website in the coming weeks. I just pushed a fairly large update to the gist.

@andrewbranch
Copy link
Member Author

I’m getting ready to merge this. There’s still more to do, but it’s complete enough to publish and iterate on.

Feedback is welcome, so I’ll mention the things I already have on my immediate to-do that I won’t get to before merging

  • Better introduction?
  • Glossary
  • Add moduleDetection, allowSyntheticDefaultImports, esModuleInterop, and verbatimModuleSyntax to the reference section. (These are discussed in other places, but they deserve detailed reference-style sections along with module and moduleResolution values.)
  • Emphasize declaration file extensions’ impact on module format in theory section
  • Cross-link to this content from other places in the website (there are redirects from deleted sections, so some will work already, but more can be added—website PRs welcome to help with this!)

@JoshuaKGoldberg
Copy link
Contributor

Amazing work, congrats @andrewbranch! 👏

One suggestion for now: have you considered putting the Theory section after Guides and References? In my experience most learners will not have the time or patience to read through all of it - they just want to learn how to fix a broken build or type errors. The guides could get them where they need to go quickly while forward-linking to references and theory as needed.

@Faithfinder
Copy link

I wonder if an appendice about baseUrl and paths belongs in there somewhere? Really made me think about it when reading
image

@bmeck
Copy link

bmeck commented Oct 18, 2023

Just a couple of feedback points:

  1. https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html#im-writing-es-modules-for-the-browser-with-no-bundler-or-module-compiler likely could also mention package.json#imports since it also can map to work with a large variety of tooling often more than paths including import maps: https://codepen.io/bradleymeck/pen/YzdmQRN

  2. https://www.typescriptlang.org/docs/handbook/modules/theory.html#scripts-and-modules-in-javascript likely should include type=module in the script tags or disambiguate Script vs Module. I think just putting type=module makes less complexity but could cause confusion when thinking there is only ESM and CJS and it not working.

@GabenGar
Copy link

GabenGar commented Oct 19, 2023

  1. As far as typescript concerned package.json#imports has no relation to paths, because the latter operates on source files, while the former - on the output files. Bundlers muddy the ground there again, since they obviously bundle the output into whatever so they are free to interpret imports whatever they want. This is not the case if you want to output nodejs ESM modules, however.
  2. The section shows how the the browser scripts were written in ye olde times, specifically how they all shared the same global scope. And there was no type=module back then.

@bmeck
Copy link

bmeck commented Oct 19, 2023

@GabenGar

  1. The intent of these sections seems to be explanatory and not solely limited to TS hence remarks about import maps. I don't think adding more ways for understanding nuance like the fact that paths differs from package.json#imports is harmful; in fact, your comment could be a great addition to the documentation since it is something coming up here under some tension.
  2. Fair enough for the script tags, but it doesn't explain how this differs from the modern day by showing the modern example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Docs The issue relates to how you learn TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.