Creating a package exclusively for types on NPM: A step-by-step guide

I'm looking to set up a package (using either a monorepo or NPM) that specifically exports types, allowing me to easily import them into my project. However, I've run into some issues with my current approach.

import type { MyType } from '@acme/types'

The structure of my project is as follows:

package.json
index.d.ts

In the index.d.ts file, I have defined the following type:

export type MyType = {
  name: string
}

And this is how my package.json looks like:

{
  "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"
  }
}

Despite setting up everything correctly, when I try to import my type, I encounter the following error:

File '/Users/acme/packages/types/index.d.ts' is not a module. ts(2306)

I even attempted renaming index.d.ts to index.ts, but unfortunately, that didn't resolve the issue either.

Answer №1

For in-depth information on the topic, refer to the TypeScript handbook's section on Publishing Declaration Files. Additionally, explore the DefinitelyTyped repository for a plethora of examples.


In a straightforward scenario like the one outlined in your query, the essential components required within the provider package are:

  1. The type declaration files, notably an entrypoint declaration file named index.d.ts.

  2. The basic criteria to fulfill the Node.js module definition include having a package.json manifest file with the necessary fields: name and version

Hence, assuming your provider package directory is situated at ./provider, the required files should be structured as follows:

./provider/package.json:

{
  "name": "@acme/types",
  "version": "0.1.0"
}

./provider/index.d.ts:

export type MyType = {
  name: string;
};

An important note from the TS handbook page linked above emphasizes that if your main declaration file is named index.d.ts and resides at the root of the package, you might not need to specify the types property explicitly, although it's recommended.

It would be wise to define the field explicitly rather than relying solely on auto-resolution behavior:

./provider/package.json:

{
  "name": "@acme/types",
  "version": "0.1.0",
  "types": "./index.d.ts"
}


This addresses the query posed — but I'll also provide a sample consumer package along with steps for replication purposes.

You can either manually recreate each example file in your filesystem by copying + pasting them or utilize the provided script in your browser's JS console to download a zip archive containing the 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 = "..."; // Base64 data truncated
  const dataUrl = createBase64DataUrl("application/zip", zipArchiveData);
  download(dataUrl, "so-75850348.zip");
})();

If the consumer package directory is placed at ./consumer, it could begin with this rudimentary package file:

./consumer/package.json:

{
  "name": "consumer",
  "version": "0.1.0"
}

To start off, install TypeScript and the provider types package as dev dependencies:

$ cd ./consumer
$ npm install --save-dev typescript ../provider

added 2 packages, and audited 4 packages in 616ms

found 0 vulnerabilities

You could have installed each package separately using distinct commands:

npm install --save-dev typescript
npm install --save-dev ../provider

Following installation, the package.json will include these dependencies:

{
  "name": "consumer",
  "version": "0.1.0",
  "devDependencies": {
    "@acme/types": "file:../provider",
    "typescript": "^5.0.2"
  }
}

To utilize the types in code, let's craft a simple TypeScript file leveraging the types from the package:

./consumer/index.ts:

import type { MyType } from "@acme/types";

const obj: MyType = { name: "foo" };

console.log(obj);

Subsequently, add an npm script to the package.json for compiling the TypeScript file with default compiler settings, naming the script compile:

{
  "name": "consumer",
  "version": "0.1.0",
  "devDependencies": {
    "@acme/types": "file:../provider",
    "typescript": "^5.0.2"
  },
  "scripts": {
    "compile": "tsc index.ts"
  }
}

Typically, configuring the compiler behavior for your project using a TSConfig is recommended.

Finally, compile the file and run it:

$ npm run compile && node index.js

> [email protected] compile
> tsc index.ts

{ name: 'foo' }

The compilation proceeds smoothly without errors, and the file executes as intended.

Note that I'm utilizing Node LTS version v18.15.0 while drafting this response.

$ node --version
v18.15.0

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Obtaining data from a TypeScript decorator

export class UploadGreetingController { constructor( private greetingFacade: GreetingFacade, ) {} @UseInterceptors(FileInterceptor('file', { storage: diskStorage({ destination: (req: any, file, cb) => { if (process.env ...

TS - Custom API hook for making multiple API requests - incompatible type with 'IUseApiHook'

What is my objective? I aim to develop a versatile function capable of handling any type of API request for a frontend application. Essentially, I want to add some flair. Issue at hand? I find myself overwhelmed and in need of a fresh perspective to revi ...

Encountering an error while running `npm start` in Angular

Attempting to utilize AngularJS with live preview using the following command: npm start Current npm version: 4.3.0 Encountering the following errors: Microsoft Windows [Version 10.0.14393] (c) 2016 Microsoft Corporation. All rights reserved. C:&b ...

After the recent update, npm version 3.5.3 is completely dysfunctional

Recently, I updated my npm to version 3.5.3 on Linux Mint 17.2 using the command: sudo npm install npm -g However, after the upgrade, it stopped working. Whenever I try to run any npm command, it just returns to the prompt without displaying any error me ...

Creating a new list by grouping elements from an existing list

I have successfully received data from my API in the following format: [ {grade: "Grade A", id: 1, ifsGrade: "A1XX", ifsType: "01XX", points: 22, type: "Type_1"}, {grade: "Grade B", id: 2, ifsGrade: &quo ...

Exploring the world of publishing Angular 2 applications

I recently created an Angular 2 application using npm, but as a beginner I am unsure of some aspects. For instance, when I publish my application, I typically use npm publish to share it on my npm account online. However, I am wondering if there is a way t ...

Setting the value in an Autocomplete Component using React-Hook-Forms in MUI

My form data contains: `agreementHeaderData.salesPerson ={{ id: "2", name: "Jhon,Snow", label: "Jhon,Snow", value: "<a href="/cdn-cgi/l/email-prot ...

Can a constructor function be utilized as a parameter type in another function within TypeScript?

Recently, I came across TypeScript and after watching some video reviews, I see great potential in it. It seems to offer better code completion, implicit code documentation, and enhanced type safety for JavaScript. I'm currently in the process of con ...

Angular 2 signal sender

I have a specific class definition for my Project: export class Project { $key: string; file: File; name: string; title: string; cat: string; url: string; progress: number; createdAt: Date = new Date(); constructor(file: File) { th ...

Combine the remaining bars by stacking the highest one on top in Highchart

Making use of stacking to display the highest value as the longest column/bar, with smaller values being merged within the highest one, can create a more visually appealing stack chart. For example, when looking at Arsenal with values of 14 and 3, ideally ...

Can all intervals set within NGZone be cleared?

Within my Angular2 component, I have a custom 3rd party JQuery plugin that is initialized in the OnInit event. Unfortunately, this 3rd party library uses setIntervals extensively. This poses a problem when navigating away from the view as the intervals rem ...

Unable to locate the specified environment variable in the current nest

Currently, I am referring to the official documentation on the NestJs website that provides a guide on using config files: https://docs.nestjs.com/techniques/configuration Below is the code snippet I am working with: app.module import { Module } from &ap ...

What could be causing npm install to not search in my designated directory?

After navigating to my projects folder in the command line which contains package.json, I attempted to run "npm install" and encountered this error message: C:\Users\kwoolley\Documents\Code\PFCU\src>npm install C:\Use ...

Alphabetically sorting objects in an array using Angular

If your TypeScript code looks something like this: items: { size: number, name: string }[] = []; ngOnInit(): void { this.items = [ { size: 3, name: 'Richard' }, { size: 17, name: 'Alex' }, ...

Ways to compel string type or disregard type?

When using the following code snippet: <Image src={user?.profilePictureUrl} alt={user?.name} /> An error is encountered: Type 'string | null | undefined' is not assignable to type 'string | StaticImport'. Type 'undefined ...

Unable to use console log in shorthand arrow function while working with Typescript

When debugging an arrow function in JavaScript, you can write it like this: const sum = (a, b) => console.log(a, b) || a + b; This code will first log a and b to the console and then return the actual result of the function. However, when using TypeSc ...

Serving sourcemaps for a web extension in Firefox: A step-by-step guide

Currently in the process of developing a web extension using TypeScript, I have encountered an issue with sourcemaps not loading properly. The use of parcel to bundle my extension has made the bundling process simple and straightforward. However, while the ...

Investigating potential causes for the crash of the Next.js Node Server: "npm ERR! code ELIFECYCLE errno 3221225477"

I recently encountered an error on node version 14.16.0, and it happened twice already. The first instance was when I created a react component using the @mui/lab/DateTimePicker. I didn't need to install it since @mui/lab was already present in the pr ...

What are the benefits of maintaining a property as non-observable instead of observable in knockout.js?

In my TypeScript project utilizing Knockout.js, I have a class with several properties. One of these properties is 'description', which is not directly tied to the DOM but needs to be used in popups triggered by certain mouse events (such as butt ...

Setting key-value pairs in TypeScript objects explained

I encountered an issue with setting key/value pairs on a plain object. type getAObjectFn = <K extends string, V>(k: K, v: V) => Record<K, V> const getAObject: getAObjectFn = (k, v) => { return { [k]: v } } console.log(getAObject ...