Skip to content

Commit

Permalink
Support .d.tss and make them visible in tests
Browse files Browse the repository at this point in the history
If a project lacks types for one or more dependencies, it is customary
to fill in those types via [ambient modules][1] in the form of `.d.ts`
(type definition) files. Unfortunately this template cannot be used to
properly provide ambient modules.

* First, because type definition files don't export anything, and ESLint
  is configured to read `*.ts` files as modules by default, ESLint will
  complain that a `.d.ts` file could be parsed as a script instead.
  That's an easy problem to solve.

* Second, TypeScript's treatment of type definition files is not
  always intuitive, and we haven't fully understood how to use them. We
  usually work with type definition files in the context of packages, so
  the mechanics of how they work are kept out of sight. In that case,
  [TypeScript will use the `types` field in `package.json`][2] to know
  how to find the type definition files for a package.

  But what about backfilling types for existing packages (i.e.
  declaration of ambient modules)? We've been assuming that as long as a
  `.d.ts` file is matched by the `include` setting in `tsconfig.json`,
  that TypeScript will be able to see and use all of the types in that
  file. For instance, if you're importing a module `foo` in one `.ts`
  file, and you've backfilled the types for `foo` in another `.d.ts`
  file in the same directory, and both files are covered by `include`,
  then TypeScript should be able to bind types to the objects you're
  importing from `foo`, right? Not always. In fact, if you have a test
  file that is *not* covered by `include` that also imports the module
  `foo`, then TypeScript seems to ignore your type definition file and
  does not associate any types with `foo`.

* Lastly, this problem illustrates that writing TypeScript and building
  TypeScript are two separate processes managed by two different tools
  (`tsserver` vs. `tsc`, respectively) which follow different rules and
  have different behavior. We need to acknowledge this in our
  configuration.

So, here's what this commit does to address these issues:

* We instruct ESLint to treat `*.d.ts` files as scripts rather than
  modules.
* We establish a `types/` directory where our ambient modules will be
  stored, and then update `tsconfig.json` to automatically consult this
  directory when resolving types for imported modules. (Note that we do
  *not* use the `typeRoots` option for this [as the `tsconfig.json`
  documentation might indicate][3]; this is [not intended to be used for
  this purpose][4]. Rather, we use [`paths`][5].)
* We update `tsconfig.json` to widen its purview for development so that
  test files have access to all of the types that non-test files do.
* We add a special `tsconfig.build.json` file that is only used by `yarn
  build` which continues to ensure that only the files we want to
  publish (i.e. files in `src/`) are emitted to `dist/`.

[1]: https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules
[2]: https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html#editing-the-packagejson
[3]: https://www.typescriptlang.org/tsconfig#typeRoots
[4]: microsoft/TypeScript#22217
[5]: https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
  • Loading branch information
mcmire committed Mar 25, 2022
1 parent a57fa0c commit 3a6fff6
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 7 deletions.
7 changes: 7 additions & 0 deletions .eslintrc.js
Expand Up @@ -9,6 +9,13 @@ module.exports = {
extends: ['@metamask/eslint-config-typescript'],
},

{
files: ['*.d.ts'],
parserOptions: {
sourceType: 'script',
},
},

{
files: ['*.js'],
parserOptions: {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -21,7 +21,7 @@
"lint": "yarn lint:eslint && yarn lint:misc --check",
"lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write",
"build:clean": "rimraf dist && yarn build",
"build": "tsc --project ."
"build": "tsc --project tsconfig.build.json"
},
"devDependencies": {
"@lavamoat/allow-scripts": "^1.0.5",
Expand Down
11 changes: 11 additions & 0 deletions tsconfig.build.json
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"declarationDir": "dist",
"rootDir": "src",
"outDir": "dist"
},
"include": ["./src/**/*.ts"],
"exclude": ["./src/**/*.test.ts"]
}
11 changes: 5 additions & 6 deletions tsconfig.json
@@ -1,17 +1,16 @@
{
"compilerOptions": {
"declaration": true,
"baseUrl": ".",
"esModuleInterop": true,
"inlineSources": true,
"lib": ["ES2020"],
"module": "CommonJS",
"moduleResolution": "Node",
"outDir": "dist",
"rootDir": "src",
"paths": {
"*": ["./types/*"]
},
"sourceMap": true,
"strict": true,
"target": "ES2017"
},
"exclude": ["./src/**/*.test.ts"],
"include": ["./src/**/*.ts"]
}
}

0 comments on commit 3a6fff6

Please sign in to comment.