@types/* in dependencies or devDependencies: Main Mistakes

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why the @types/* Question Haunts Even Experienced Developers?



Have you ever spent an hour debugging an error that ultimately boiled down to incorrectly placing an @types/* package in package.json? If so — you are not alone. The question "How do I decide whether @types/* goes into dependencies or devDependencies?" has gathered thousands of views on Stack Overflow and has become a real headache for the TypeScript community.



It seems simple, right? Throw types into devDependencies and be done with it. But it's not that simple. When your project starts growing, third-party libraries, monorepos, and CI/CD pipelines get involved, incorrect placement of types can lead to fatal build errors, unexpected crashes in production, or, even worse, a silent loss of type checking.



In this article, we will break down the top 5 most common mistakes when working with @types/*, learn how to make the right decision based on real-world scenarios, and create a checklist that will protect you from problems. Ready? Let's go!



Mistake #1: "All @types/* always go in devDependencies" — A Myth That Breaks Builds



The most popular advice on the internet goes like this: "Types are not runtime code, so they should always be in devDependencies." And this is indeed true in 90% of cases. But there is an important exception that people forget about.



When devDependencies is the right choice

If you are writing an application (an Express web server, a React frontend, a command-line utility), then feel free to put all @types/* into devDependencies. Your end user does not compile TypeScript — they run already compiled JavaScript. Types are only needed by you during development.



// Example for an Express application{  "devDependencies": {    "@types/express": "^4.17.21",    "@types/node": "^20.11.0",    "typescript": "^5.3.3"  }}


When devDependencies is a fatal mistake

Now imagine this: you are developing a library that exports TypeScript types for external consumers. If your library's types depend on @types/express, and you put it in devDependencies, then a user who installs your library will not automatically get those types. And their IDE will start showing red underlines.



// ❌ Incorrect for a library that exports types{  "devDependencies": {    "@types/express": "^4.17.21" // The user will not see these types!  }}

// ✅ Correct{ "dependencies": { "@types/express": "^4.17.21" // Types will be available to consumers }}


Golden rule: If your library re-exports types from an @types/* package or uses them in its public API, then that package should be in dependencies. If the types are only used internally — devDependencies.



Mistake #2: Ignoring the Difference Between "Using in Code" and "Using in Types"



Many developers get confused: "I'm importing Request from express, so express should be in dependencies." This is true for the express package itself, but not for its types.



Let's break it down with a specific example. There are three scenarios for using the @types/lodash package:



  • Scenario A: You write import _ from 'lodash' and use _.chunk(). Types are only needed for compilation — devDependencies.
  • Scenario B: You create a function function mySort(arr: T[]): T[] and use _.orderBy inside. Types do not leak out — devDependencies.
  • Scenario C: You write export function proc

Blogs

Book Recommendations