I would like to create a package (monorepo or NPM) that export only types so that in my project I can import them as such
import type { MyType } from '@acme/types'
I have tried the following solution but it doesn't work
Project structure
package.json
index.d.ts
index.d.ts
export type MyType = {
name: string
}
package.json
{
"name": "@acme/types",
"main": "./index.d.ts",
"types": "./index.d.ts",
"exports": {
".": {
"default": "./index.d.ts",
"import": "./index.d.ts",
"types": "./index.d.ts"
},
"./package.json": "./package.json"
}
}
When I want to import my type, I get this error
File '/Users/acme/packages/types/index.d.ts' is not a module. ts(2306)
I have also tried to rename index.d.ts
to index.ts
. Doesn't work either
See Declaration Files > Publishing in the TypeScript handbook for information related to this topic, and check out the DefinitelyTyped repository for lots of examples.
In a simple scenario like the one described by the details in your question, the only parts needed in the provider package are:
the type declaration files, including an entrypoint declaration file named index.d.ts
, and
the minimum criteria for meeting the definition of a Node.js module: a package.json
manifest file with the required fields: name
and version
So, given that your provider package directory is located at ./provider
, the required files would look like this:
./provider/package.json
:
{
"name": "@acme/types",
"version": "0.1.0"
}
./provider/index.d.ts
:
export type MyType = {
name: string;
};
The following note is included in the linked TS handbook page, in the section Including declarations in your npm package:
Also note that if your main declaration file is named
index.d.ts
and lives at the root of the package you do not need to mark thetypes
property, though it is advisable to do so.
So, you might consider defining that field explicitly rather than relying on automatic resolution behavior:
./provider/package.json
:
{
"name": "@acme/types",
"version": "0.1.0",
"types": "./index.d.ts"
}
Now, that's the answer to the question asked — but I'll also include an example consumer package with reproduction steps for demonstration.
If you want to follow along, you can copy + paste each of the following example files, recreating them in your filesystem. Or you can just run this script in your browser's JS console to download a zip archive of the entire project structure:
(() => { function createBase64DataUrl(mediaType, b64Str) { return `data:${mediaType};base64,${b64Str}`; } function download(url, fileName) { const a = document.createElement("a"); a.href = url; a.download = fileName; a.click(); a.remove(); } const zipArchiveData = "UEsDBAoAAAAAADQOe1YAAAAAAAAAAAAAAAAJABwAcHJvdmlkZXIvVVQJAAODPCFkjjwhZHV4CwABBPUBAAAEFAAAAFBLAwQUAAAACABObHtWF0wQhEEAAABNAAAAFQAcAHByb3ZpZGVyL3BhY2thZ2UuanNvblVUCQADs+EhZLPhIWR1eAsAAQT1AQAABBQAAACr5lJQUMpLzE1VslJQckhMzk3VL6ksSC1W0gFJlKUWFWfm54HkDPQM9QwgohAFQDE9/cy8lNQKvRS9kmIlrlouAFBLAwQUAAAACAA5DntWXPRvjCkAAAAqAAAAEwAcAHByb3ZpZGVyL2luZGV4LmQudHNVVAkAA448IWSPPCFkdXgLAAEE9QEAAAQUAAAAS60oyC8qUSipLEhV8K0MAVG2CtVcCgp5ibmpVgrFJUWZeenWXLXWXABQSwMECgAAAAAA53R7VgAAAAAAAAAAAAAAAAkAHABjb25zdW1lci9VVAkAA+HwIWTk8CFkdXgLAAEE9QEAAAQUAAAAUEsDBBQAAAAIANdye1bERSpvhQAAAL8AAAAVABwAY29uc3VtZXIvcGFja2FnZS5qc29uVVQJAAMF7SFkBe0hZHV4CwABBPUBAAAEFAAAAE2NvQrDMAwGdz+F0VwUt9AlU4c+RyHIKhjqH2zXNIS8e2WnQ9fvTqdNaQ1h8QyzBoqhvD1nOPW1cS4uhg4MntEcq+V258TBciDHRegms4DbQp6nuqYxwtO9eEacUo7N2V9TtCFQdql263FFgxcQtI/6Qf6qFH2SUndrIe3k7wdF6BdqV19QSwMEFAAAAAgAcXB7VscThVtUAAAAZAAAABEAHABjb25zdW1lci9pbmRleC50c1VUCQADhughZIfoIWR1eAsAAQT1AQAABBQAAADLzC3ILypRKKksSFWoVvCtDAExahXSivJzFZQcEpNzU/VBcsVK1lxcyfl5xSUK+UlZVjCFtkA9eYm5qVYKSmn5+UoKtVBV+Tmpejn56RpAtZrWXABQSwECHgMKAAAAAAA0DntWAAAAAAAAAAAAAAAACQAYAAAAAAAAABAA7UEAAAAAcHJvdmlkZXIvVVQFAAODPCFkdXgLAAEE9QEAAAQUAAAAUEsBAh4DFAAAAAgATmx7VhdMEIRBAAAATQAAABUAGAAAAAAAAQAAAKSBQwAAAHByb3ZpZGVyL3BhY2thZ2UuanNvblVUBQADs+EhZHV4CwABBPUBAAAEFAAAAFBLAQIeAxQAAAAIADkOe1Zc9G+MKQAAACoAAAATABgAAAAAAAEAAACkgdMAAABwcm92aWRlci9pbmRleC5kLnRzVVQFAAOOPCFkdXgLAAEE9QEAAAQUAAAAUEsBAh4DCgAAAAAA53R7VgAAAAAAAAAAAAAAAAkAGAAAAAAAAAAQAO1BSQEAAGNvbnN1bWVyL1VUBQAD4fAhZHV4CwABBPUBAAAEFAAAAFBLAQIeAxQAAAAIANdye1bERSpvhQAAAL8AAAAVABgAAAAAAAEAAACkgYwBAABjb25zdW1lci9wYWNrYWdlLmpzb25VVAUAAwXtIWR1eAsAAQT1AQAABBQAAABQSwECHgMUAAAACABxcHtWxxOFW1QAAABkAAAAEQAYAAAAAAABAAAApIFgAgAAY29uc3VtZXIvaW5kZXgudHNVVAUAA4boIWR1eAsAAQT1AQAABBQAAABQSwUGAAAAAAYABgAEAgAA/wIAAAAA"; const dataUrl = createBase64DataUrl("application/zip", zipArchiveData); download(dataUrl, "so-75850348.zip"); })();
Given that the consumer package directory is located at ./consumer
, it might start with this minimal package file:
./consumer/package.json
:
{
"name": "consumer",
"version": "0.1.0"
}
The first step is to install TypeScript and the provider types package as development dependencies:
$ cd ./consumer
$ npm install --save-dev typescript ../provider
added 2 packages, and audited 4 packages in 616ms
found 0 vulnerabilities
Each package could have been installed using separate commands:
npm install --save-dev typescript npm install --save-dev ../provider
Afterward, the package.json
will include these dependencies:
{
"name": "consumer",
"version": "0.1.0",
"devDependencies": {
"@acme/types": "file:../provider",
"typescript": "^5.0.2"
}
}
We want to use the types in some code, so let's create a basic TypeScript file which will use the types from the package:
./consumer/index.ts
:
import type { MyType } from "@acme/types";
const obj: MyType = { name: "foo" };
console.log(obj);
Next, let's add an npm script to the package.json
file which will compile the TypeScript file using the compiler defaults — we'll name the script compile
:
{
"name": "consumer",
"version": "0.1.0",
"devDependencies": {
"@acme/types": "file:../provider",
"typescript": "^5.0.2"
},
"scripts": {
"compile": "tsc index.ts"
}
}
In most scenarios you'll probably want to configure the compiler behavior for your project using a TSConfig.
Now, let's compile the file and run it:
$ npm run compile && node index.js
> [email protected] compile
> tsc index.ts
{ name: 'foo' }
Compilation succeeds without issue and the file runs as expected.
Note that I'm using this Node LTS version as I write this answer:
$ node --version v18.15.0