Question
When to Put @types Packages in dependencies vs devDependencies in TypeScript
Question
I am using TypeScript in a project and want to use a JavaScript library along with its type definitions from @types/*.
For example, I can install the types with:
npm install @types/some-library
I am unsure whether these packages should be saved in dependencies or devDependencies.
My assumption is that @types/* packages usually belong in devDependencies because they are needed during development and compilation, not at runtime. However, I often see @types/* listed under dependencies in real projects, which makes this confusing.
How should I decide whether an @types/* package belongs in dependencies or devDependencies? Are there any commonly accepted or official guidelines for this?
Short Answer
By the end of this page, you will understand what @types/* packages are, why they usually do not affect runtime, and how to choose between dependencies and devDependencies based on whether your project is an application or a published library. You will also learn the common exceptions and mistakes developers make when managing TypeScript type packages.
Concept
@types/* packages provide TypeScript type definitions for JavaScript libraries that do not ship their own types.
For example, if a library is written in plain JavaScript, TypeScript cannot automatically know the shapes of its functions, objects, or classes. An @types/* package fills in that missing information so your editor and compiler can understand how to use the library safely.
Why this matters
Types help with:
- autocomplete in editors
- compile-time error checking
- clearer APIs
- safer refactoring
But an important detail is this:
- Type definitions are usually used only during development/build time
- They are usually not needed when your code runs in production
That is why @types/* packages often go into devDependencies.
The main rule
A good beginner rule is:
- Put
@types/*indevDependenciesfor normal applications - Put
@types/*independenciesonly when downstream users need them to use your published package correctly
Why the distinction exists
In package.json:
Mental Model
Think of runtime code and type definitions as two different kinds of luggage:
dependenciesare the things you must bring on the trip because you will actually use them there.devDependenciesare the planning tools you use before the trip, like a map or checklist.
@types/* packages are usually like the map: they help you prepare, but they are not something the running JavaScript program uses directly.
There is one exception: if you are packing for someone else too—meaning you are publishing a library for other developers—then some type packages may need to travel with the package because your users rely on them to understand your API.
Syntax and Examples
The decision is made in package.json.
Example: app project
If you are building an application, this is the usual setup:
{
"dependencies": {
"express": "^4.19.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"@types/express": "^5.0.0"
}
}
Here:
expressis needed at runtime@types/expressis needed only by TypeScript during development/build
Example: install as a dev dependency
npm install express
npm install -D typescript @types/express
Example: published library
Suppose you publish a TypeScript library and your public API exposes express types:
Step by Step Execution
Consider this small TypeScript app:
import express from "express";
const app = express();
app.get("/", (req, res) => {
res.send("Hello");
});
And this package.json:
{
"dependencies": {
"express": "^4.19.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"@types/express": "^5.0.0"
}
}
What happens step by step
-
You install packages
expressprovides the real runtime code.
Real World Use Cases
1. Backend application
A Node.js API using express, cors, or jsonwebtoken may install:
- runtime packages in
dependencies @types/*packages indevDependencies
This is the most common setup.
2. Frontend application
A React or Vite app may use packages that already ship their own types, but older JavaScript-only packages may still require @types/*.
In an app, those type packages are usually development-only.
3. Internal company tool
For scripts, build tools, migrations, and automation tasks, the same rule applies:
- runtime code in
dependencies - type-only packages in
devDependencies
4. Published npm library
If you publish a library and your .d.ts output or exported API references types from another package, consumers may need those types when they install your package.
That is where dependencies can make sense for type packages.
5. DefinitelyTyped-based ecosystem packages
Real Codebase Usage
In real projects, developers usually follow a few patterns.
Pattern 1: Application code
For apps, @types/* almost always goes into devDependencies.
Common example:
{
"dependencies": {
"pg": "^8.11.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"@types/node": "^20.0.0"
}
}
Why:
- the app runs compiled JavaScript
- the types are only for local development, CI, and builds
Pattern 2: Public API uses external types
Library authors often check whether exported types reference another package.
Example:
import { IncomingMessage } from "http";
{
: ;
}
Common Mistakes
Mistake 1: Putting every @types/* package in dependencies
This makes your runtime dependency list larger than necessary.
{
"dependencies": {
"express": "^4.19.0",
"@types/express": "^5.0.0"
}
}
For a normal app, this is usually unnecessary.
Better
{
"dependencies": {
"express": "^4.19.0"
},
"devDependencies": {
"@types/express": "^5.0.0"
}
}
Mistake 2: Installing @types/* for a package that already includes types
Comparisons
| Situation | Put @types/* in devDependencies? | Put @types/* in dependencies? | Why |
|---|---|---|---|
| Private app | Yes, usually | Rarely | Types are only needed during development/build |
| Backend server | Yes, usually | Rarely | Runtime uses JS package, not type package |
| Frontend app | Yes, usually | Rarely | Build tools use the types, browser does not |
| Published library with internal-only types | Yes, often | Sometimes not needed | Consumers do not depend on those types directly |
| Published library with exported external types | Sometimes no |
Cheat Sheet
@types/*packages provide TypeScript type definitions for JavaScript packages.- They are usually used at development/build time, not runtime.
- For most applications, put
@types/*indevDependencies. - For published libraries, put
@types/*independenciesonly if consumers need them for your exported API. - Always check whether the main package already ships its own types.
Common commands
npm install some-library
npm install -D @types/some-library
Rule of thumb
- App:
@types/*→devDependencies - Library with public type exposure:
@types/*→ maybedependencies
Examples
{
"dependencies": {
"express": "^4.19.0"
},
FAQ
Should @types/* usually go in devDependencies?
Yes. For most application projects, @types/* packages belong in devDependencies because they are used during development and compilation, not at runtime.
When should @types/* go in dependencies?
Usually when you publish a library and your consumers need those types because your public API exposes them.
Why do I sometimes see @types/* in dependencies?
Because some packages are libraries, not apps, and their users need those type definitions to type-check correctly.
Are @types/* packages used at runtime?
Normally no. They exist for TypeScript tooling, editor support, and compilation.
What if the package already includes its own types?
Then you usually do not need a separate @types/* package.
Is @types/node also usually a dev dependency?
Yes, in most apps it is a devDependency because it helps TypeScript understand Node.js APIs during development.
Is there an official universal rule?
Mini Project
Description
Create a small TypeScript workspace with two parts: an application and a reusable library. The purpose is to practice deciding where type packages belong and to see why the answer changes depending on whether code is private app code or a published package API.
Goal
Build one app and one library, then place @types/* packages in the correct dependency section for each case.
Requirements
- Create a small TypeScript app that uses
express. - Add the necessary type packages and place them in the correct section of
package.json. - Create a small reusable TypeScript library that exports a type or function based on
expresstypes. - Update the library
package.jsonso its type-related dependencies match its public API. - Verify that both projects can type-check successfully.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular Dependency Injection: Fix "Can't Resolve All Parameters for Component" Errors
Learn why Angular shows "Can't resolve all parameters for component" and how to fix service injection issues in components.