!!! Important! This repo has recently changed layout! !!!
Definitely Typed has recently changed to a proper pnpm monorepo; you may want to reread this document for changes to the layout of packages in this repo.
At the very least, you may want to git clean -fdx the repo (or node ./scripts/clean-node-modules.js on Windows) to clean up node_modules and run pnpm install --filter . to install the workspace root. See further sections for more info on pnpm install.
Current status
This section tracks the health of the repository and publishing process.
It may be helpful for contributors experiencing any issues with their PRs and packages.
For an npm package “foo”, typings for it will be at “@types/foo”.
If your package has typings specified using the types or typings key in its package.json, the npm registry will display that the package has available bindings like so:
If you still can’t find the typings, just look for any “.d.ts” files in the package and manually include them with a /// <reference path="" />.
Support Window
Definitely Typed only tests packages on versions of TypeScript that are less than 2 years old.
Older versions of TypeScript
@types packages have tags for versions of TypeScript that they explicitly support, so you can usually get older versions of packages that predate the 2-year window.
For example, if you run npm dist-tags @types/react, you’ll see that TypeScript 2.5 can use types for react@16.0, whereas TypeScript 2.6 and 2.7 can use types for react@16.4:
Tag
Version
latest
16.9.23
ts2.0
15.0.1
…
…
ts2.5
16.0.36
ts2.6
16.4.7
ts2.7
16.4.7
…
…
TypeScript 1.*
Manually download from the master branch of this repository and place them in your project
Typings (use preferred alternatives, typings is deprecated)
NuGet (use preferred alternatives, nuget DT type publishing has been turned off)
Definitely Typed only works because of contributions by users like you!
Testing
Before you share your improvement with the world, use the types yourself by creating a typename.d.ts file in your project and filling out its exports:
declare module "libname" {
// Types inside here
export function helloWorldMessage(): string;
}
Test editing an existing package
You can edit the types directly in node_modules/@types/foo/index.d.ts to validate your changes, then bring the changes to this repo with the steps below.
Alternatively, you can use module augmentation to extend existing types from the DT module or use the declare module technique above which will override the version in node_modules.
Adding tests to a new package
Add to your tsconfig.json:
"baseUrl": "types",
"typeRoots": ["types"],
Create types/foo/index.d.ts containing declarations for the module “foo”.
You should now be able to import from "foo" in your code and it will route to the new type definition.
Then build and run the code to make sure your type definition actually corresponds to what happens at runtime.
The Definitely Typed repo is large; you may want to consider using a “blobless clone” to save time and space by passing --filter=blob:none when running git clone.
pnpm install will install the entire
repository, including packages you may not be editing. If you’d like to install only a subset,
you can run pnpm install -w --filter "{./types/foo}..." to install @types/foo and all of
its dependencies. If you need to run tests for packages that depend on @types/foo, you can run pnpm install -w --filter "...{./types/foo}..." to pull in all related packages for testing.
[!NOTE]
If you are using Windows, you may find that git clean does not remove the node_modules directory or hangs when doing so. If you need to remove node_modules, you can run pnpm clean-node-modules to reset the repo.
We use a bot to let a large number of pull requests to DefinitelyTyped be handled entirely in a self-service manner. You can read more about why and how here. Here is a handy reference showing the life cycle of a pull request to DT:
When you make a PR to edit an existing package, dt-bot should @-mention the package’s owners.
If it doesn’t, you can do so yourself in the comment associated with the PR.
Create a new package
If you are the library author and your package is written in TypeScript, bundle the generated declaration files in your package instead of publishing to Definitely Typed.
You can also generate declaration files from JavaScript files, using JSDoc for type annotations.
If you are adding typings for an npm package, create a directory with the same name.
If the package you are adding typings for is not on npm, make sure the name you choose for it does not conflict with the name of a package on npm.
(You can use npm info <my-package> to check for the existence of the <my-package> package.)
When a package bundles its own types, types should be removed from Definitely Typed to avoid confusion.
You can remove it by running pnpm run not-needed <typingsPackageName> <asOfVersion> [<libraryName>].
<typingsPackageName>: This is the name of the directory to delete.
<asOfVersion>: A stub will be published to @types/<typingsPackageName> with this version. Should be higher than any currently published version and should be a version of <libraryName> on npm.
<libraryName>: Name of npm package that replaces the Definitely Typed types. Usually this is identical to <typingsPackageName>, in which case you can omit it.
If a package was never on Definitely Typed, it does not need to be added to notNeededPackages.json.
Running tests
Test your changes by running pnpm test <package to test> where <package to test> is the name of your package.
You need to run this from the DefinitelyTyped directory because individual package.jsons don’t define test scripts.
This script uses dtslint to run the TypeScript compiler against your dts files.
Once you have all your changes ready, use pnpm run test-all to see how your changes affect other modules.
@arethetypeswrong/cli (attw) checks
dtslint includes module format and package.json configuration checks from @arethetypeswrong/cli. The checks run only if a SemVer-major-compatible implementation package can be found on npm to compare against the DefinitelyTyped package. (DefinitelyTyped packages marked as nonNpm in their package.json are skipped.)
Many packages currently fail the attw checks and need to be fixed. To allow us to make incremental progress, failed attw checks do not fail the dtslint run when the package is listed in failingPackages in attw.json, but they will still be reported in the pnpm test my-package output. If you fix the package, remove it from failingPackages so that attw checks can start failing dtslint runs.
All problems reported by attw have documentation linked in the output. Some rules of thumb to help avoid problems:
The package.json in the DefinitelyTyped package must have matching type and exports fields if the implementation package uses them in its package.json. For example, if an implementation package.json looks like:
Notice that each exports subpath is reflected, and each JavaScript file has a corresponding declaration file with a matching file extension—a .d.ts file types a .js file, not a .mjs or .cjs file!
When the implementation package uses module.exports = ..., the DefinitelyTyped package should use export =, not export default. (Alternatively, if the module.exports is just an object of named properties, the DefinitelyTyped package can use a series of named exports.) The most common obstacle to correcting this problem is confusion about how to export types in addition to the primary export. For example, assume these types are incorrectly using export default:
Changing the export default to an export = creates an error:
export interface Options {
// ...
}
declare function doSomething(options: Options): void;
export = doSomething;
// ^^^^^^^^^^^^^^^^^
// Error: An export assignment cannot be used in a module with other exported elements.
To fix this, move the types inside a namespace with the same name as the function:
If you are adding typings for an npm package, create a directory with the same name.
If the package you are adding typings for is not on npm, set "nonNpm": true in the package.json, and make sure the name you choose for it does not conflict with the name of a package on npm.
(You can use npm info <my-package> to check for the existence of the <my-package> package.)
In rare occasions, nonNpm may be set to "conflict", which incidates that there is a package on npm with the same name, but the types intentionally conflict with that package.
This can be true for packages which define an environment like @types/node or for dummy packages like aws-lambda. Avoid using "conflict" where possible.
<my-package>-tests.ts
There should be a <my-package>-tests.ts file, which is considered your test file, along with any *.ts files it imports.
If you don’t see any test files in the module’s folder, create a <my-package>-tests.ts.
These files are used to validate the API exported from the *.d.ts files which are shipped as @types/<my-package>.
They do not themselves ship.
Changes to the *.d.ts files should include a corresponding *.ts file change which shows the API being used, so that someone doesn’t accidentally break code you depend on.
For example, this change to a function in a .d.ts file adding a new param to a function:
index.d.ts:
- export function twoslash(body: string): string
+ export function twoslash(body: string, config?: { version: string }): string
<my-package>-tests.ts:
import {twoslash} from "./"
// $ExpectType string
const result = twoslash("//")
+ // Handle options param
+ const resultWithOptions = twoslash("//", { version: "3.7" })
+ // When the param is incorrect
+ // @ts-expect-error
+ const resultWithOptions = twoslash("//", { })
If you’re wondering where to start with test code, the examples in the README of the original package are a great place to start.
You can validate your changes with npm test <package to test> from the root of this repo, which takes changed files into account.
Use $ExpectType to assert that an expression is of a given type and @ts-expect-error to assert that a compile error. Examples:
You can still disable rules with an .eslintrc.json, but should not in new packages.
Disabling rules for the entire package makes it harder to review.
tsconfig.json
tsconfig.json should have noImplicitAny, noImplicitThis, strictNullChecks and strictFunctionTypes set to true.
You may edit the tsconfig.json to add new test files, to add "target": "es6" (needed for async functions), to add to "lib" or to add the "jsx" compiler option.
esModuleInterop/allowSyntheticDefaultImports
TL;DR: esModuleInterop and allowSyntheticDefaultImports are not allowed in your tsconfig.json.
These options make it possible to write a default import for a CJS export, modeling the built-in interoperability between CJS and ES modules in Node and in some JS bundlers:
// component.d.ts
declare class Component {}
// CJS export, modeling `module.exports = Component` in JS
export = Component;
// index.d.ts
// ESM default import, only allowed under 'esModuleInterop' or 'allowSyntheticDefaultExports'
import Component from "./component";
Since the compile-time validity of the import in index.d.ts is dependent upon specific compilation settings, which users of your types do not inherit, using this pattern in DefinitelyTyped would force users to change their own compilation settings, which might be incorrect for their runtime. Instead, you must write a CJS import for a CJS export to ensure widespread, config-independent compatibility:
If the implementation package uses ESM and specifies "type": "module", then you should modify package.json to match:
{
"type": "module"
}
This also applies if the implementation package has exports in its package.json.
Peer dependencies
Definitely Typed allows peerDependencies in package.json.
Peer dependencies can help prevent situations where a package manager unexpectedly installs too-new versions or more than one version of the same package.
However, peer dependencies have downsides; package managers differ in their handling of peer dependencies (e.g., yarn does not auto-install them, npm requires --legacy-peer-deps for mismatches).
As such, PRs introducing new peer dependencies require maintainer approval and should be limited to specific circumstances.
In general, types packages should only have a peer dependency if the upstream package has a peer dependency on the same package (or its types).
For example, a DT package for a React component can specify a peer dependency on @types/react@*, as the consumer will have needed to install @types/react to use JSX in the first place.
If the consumer installs @types/react@16 in their project, but a newer version of @types/react is available on npm, the peer dependency may help the package manager choose @types/react@16 instead of that newer version.
Similarly, chai-as-promised has a peer dependency on chai, so @types/chai-as-promised should have a peer dependency on @types/chai.
There are some cases where the upstream package does not have a peer dependency on the types package, but a peer dependency is still appropriate.
These are typically cases where the upstream package extends another package and assumes it exists, so should have declared a peer dependency as it extends another package, but did not.
For example, chai-match-pattern extends chai, but does not declare a peer dependency on chai, but needs it to function. @types/chai-match-pattern should have a peer dependency on @types/chai.
If a package simply exposes types from another package as a part of its API due to a regular dependency in the upstream package, it should not use a peer dependency.
For example, express has qs in its "dependencies". When users install express, they don’t need to manually install qs. Likewise, @types/express has @types/qs in its "dependencies".
It would be incorrect to declare @types/qs as a peer dependency of @types/express, since that would require some downstream consumers to manually install @types/qs.
.npmignore
This file defines which files are to be included in each @types package. It must take a specific form. For packages with only one version in the repo:
*
!**/*.d.ts
!**/*.d.cts
!**/*.d.mts
!**/*.d.*.ts
Which is to say “ignore all files, but don’t ignore any declaration files”. For packages that have more than one version in the repo, the “latest” version (at the top level) should contain something like:
Which is the same as the previous .npmignore but ignoring each of the versioned child directories.
CI will fail if this file contains the wrong contents and provide the intended value. No matter what this file contains, the publisher will only publish declaration files.
Formatting: dprint is set up on this repo, so you can run pnpm dprint fmt -- 'path/to/package/**/*.ts'.
Consider using the VS Code .vscode/settings.template.json (or equivalent for other editors) to format on save with the VS Code dprint extension
function sum(nums: number[]): number: Use ReadonlyArray if a function does not write to its parameters.
interface Foo { new(): Foo; }:
This defines a type of objects that are new-able. You probably want declare class Foo { constructor(); }.
const Class: { new(): IClass; }:
Prefer to use a class declaration class Class { constructor(); } instead of a new-able constant.
getMeAT<T>(): T:
If a type parameter does not appear in the types of any parameters, you don’t really have a generic function, you just have a disguised type assertion.
Prefer to use a real type assertion, e.g. getMeAT() as number.
Example where a type parameter is acceptable: function id<T>(value: T): T;.
Example where it is not acceptable: function parseJson<T>(json: string): T;.
Exception: new Map<string, number>() is OK.
Using the types Function and Object is almost never a good idea. In 99% of cases it’s possible to specify a more specific type. Examples are (x: number) => number for functions and { x: number, y: number } for objects. If there is no certainty at all about the type, any is the right choice, not Object. If the only known fact about the type is that it’s some object, use the type object, not Object or { [key: string]: any }.
var foo: string | any:
When any is used in a union type, the resulting type is still any. So, while the string portion of this type annotation may look useful, it in fact offers no additional typechecking over simply using any.
Depending on the intention, acceptable alternatives could be any, string or string | object.
Definition owners
TL;DR: do not modify .github/CODEOWNERS, always modify list of the owners in package.json.
DT has the concept of “Definition Owners” which are people who want to maintain the quality of a particular module’s types.
Adding yourself to the list will cause you to be notified (via your GitHub username) whenever someone makes a pull request or issue about the package.
Your PR reviews will have a higher precedence of importance to the bot which maintains this repo.
The DT maintainers are putting trust in the definition owners to ensure a stable eco-system, please don’t add yourself lightly.
To add yourself as a Definition Owner, modify the owners array in package.json:
Note that this list is not used to provide credit for contributions; it is only used for managing PR reviews.
Once a week the Definition Owners are synced to the file .github/CODEOWNERS which is our source of truth.
The history of Definitely Typed
Definitely Typed is one of the most active repositories on GitHub. You might have wondered how the project came to be. @johnnyreilly wrote a history of Definitely Typed. It tells the story of the early days of Definitely Typed, from a repository created by @borisyankov, to the point where it became a pivotal part of the TypeScript ecosystem. You can read the story of Definitely Typed here.
FAQ
What exactly is the relationship between this repository and the @types packages on npm?
The master branch is automatically published to the @types scope on npm thanks to DefinitelyTyped-tools.
I’ve submitted a pull request. How long until it is merged?
It depends, but most pull requests will be merged within a week.
Some PRs can be merged by the owners of a module and they can be merged much faster.
Roughly:
PRs which only change the types of a module and have corresponding tests changes will be merged much faster
PRs that have been approved by an owner listed in the definition’s package.json are usually merged more quickly; PRs for new definitions will take more time as they require more review from maintainers. Each PR is reviewed by a TypeScript or Definitely Typed team member before being merged, so please be patient as human factors may cause delays. Check the Pull Request Status Board to see progress as maintainers work through the open PRs.
I’d like to submit a change to a very popular project, why are they treated differently?
For changes to very popular modules, e.g. Node/Express/Jest which have many millions of downloads each per week on npm, the requirements for contributions are a bit higher.
Changes to these projects can have massive ecosystem effects and so we treat changes to them with a lot of care.
These modules require both a sign-off from a DT maintainer and enthusiastic support from the module owners. The bar for passing this can be quite high and often PRs can go stale because it doesn’t have a champion.
If you’re finding that no-one is committing, try to make your PR have a smaller focus.
My PR is merged; when will the @types npm package be updated?
I’m writing a definition that depends on another definition. Should I use <reference types="" /> or an import?
If the module you’re referencing is a module (uses export), use an import.
If the module you’re referencing is an ambient module (uses declare module) or just declares globals, use <reference types="" />.
Some packages have a tsconfig.json that is missing "noImplicitAny": true, "noImplicitThis": true or "strictNullChecks": true.
Then they are wrong and we’ve not noticed yet. You can help by submitting a pull request to fix them.
Alternatively, you can enable a git hook which will format your code automatically. Run pnpm run setup-hooks. Then, when you commit, dprint fmt command will be executed on changed files.
Pull requests do not require correct formatting to be merged.
Any unformatted code will be automatically reformatted after being merged.
💡 If you’re a VS Code user, we suggest copying the .vscode/settings.template.json file to .vscode/settings.json.
That template sets the dprint VS Code extension as the default formatter in the repo.
If types are part of a web standard, they should be contributed to TypeScript-DOM-lib-generator so that they can become part of the default lib.dom.d.ts.
What about type definitions with no matching package?
If there’s no source JavaScript code at all, for example if you’re writing helper types or types for a spec, you should publish the types yourself, not on Definitely Typed.
Because they’re meant to provide types for existing JavaScript code, @types packages are not meant to be imported directly.
That is, you shouldn’t create a Definitely Typed package that’s meant to be used like import type { ... } from "@types/foo".
Nor should you expect to write import type { ... } from "foo" when there’s no foo installed.
This is different from providing types for a browser only JavaScript library or types for an entire environment like node, bun, et al.
There, the types are either resolved implicitly or using /// <references types="foo" />.
Should I add an empty namespace to a package that doesn’t export a module to use ES6 style imports?
Importing this module with an ES6 style import in the form import * as foo from "foo"; leads to the error:
error TS2497: Module ‘foo’ resolves to a non-module entity and cannot be imported using this construct.
This error can be suppressed by merging the function declaration with an empty namespace of the same name, but this practice is discouraged.
This is a commonly cited Stack Overflow answer regarding this matter.
It is more appropriate to import the module using the import foo = require("foo"); syntax.
Nevertheless, if you want to use a default import like import foo from "foo"; you have two options:
you can use the --allowSyntheticDefaultImports compiler option if your module runtime supports an interop scheme for non-ECMAScript modules, i.e. if default imports work in your environment (e.g. Webpack, SystemJS, esm).
you can use the --esModuleInterop compiler option if you want TypeScript to take care of non-ECMAScript interop (since TypeScript 2.7).
A package uses export =, but I prefer to use default imports. Can I change export = to export default?
Do not change the type definition if it is accurate.
For an npm package, export = is accurate if node -p 'require("foo")' works to import a module and export default is accurate if node -p 'require("foo").default' works to import a module.
I want to use features from very new TypeScript versions.
Then you will have set the minimum supported version by specifying "minimumTypeScriptVersion": "X.Y" in package.json.
However, if your project needs to maintain types that are compatible with, say, 3.7 and above at the same time as types that are compatible with 3.6 or below, you will need to use the typesVersions feature.
You can find a detailed explanation of this feature in the official TypeScript documentation.
Create the sub-directory mentioned in the typesVersions field inside your types directory (ts3.6/ in this example).
ts3.6/ will support TypeScript versions 3.6 and below, so copy the existing types and tests there.
Back in the root of the package, add the TypeScript 3.7 features you want to use.
When people install the package, TypeScript 3.6 and below will start from ts3.6/index.d.ts, whereas TypeScript 3.7 and above will start from index.d.ts.
I want to add a DOM API not present in TypeScript by default.
This may belong in TypeScript-DOM-lib-generator. See the guidelines there.
If the standard is still a draft, it belongs here.
Use a name beginning with dom- and include a link to the standard as the “Project” link in package.json.
When it graduates draft mode, we may remove it from Definitely Typed and deprecate the associated @types package.
How do Definitely Typed package versions relate to versions of the corresponding library?
NOTE: The discussion in this section assumes familiarity with Semantic versioning
Each Definitely Typed package is versioned when published to npm.
The DefinitelyTyped-tools (the tool that publishes @types packages to npm) will set the declaration package’s version by using the major.minor.9999 version number listed in package.json.
For example, here are the first few lines of Node’s type declarations for version 20.8.x at the time of writing:
Because the version is listed as 20.8.9999, the npm version of the @types/node package will also be 20.8.x.
Note that the version in package.json should only contain major.minor version (e.g. 10.12) followed by .9999.
This is because only the major and minor release numbers are aligned between library packages and type declaration packages. (The .9999 is to ensure that local @types packages are always newest during local development.)
The patch release number of the type declaration package (e.g. .0 in 20.8.0) is initialized to zero by Definitely Typed and is incremented each time a new @types/node package is published to npm for the same major/minor version of the corresponding library.
Sometimes type declaration package versions and library package versions can get out of sync.
Below are a few common reasons why, in order of how much they inconvenience users of a library.
Only the last case is typically problematic.
As noted above, the patch version of the type declaration package is unrelated to the library patch version.
This allows Definitely Typed to safely update type declarations for the same major/minor version of a library.
If updating a package for new functionality, don’t forget to update the version number to line up with that version of the library.
If users make sure versions correspond between JavaScript packages and their respective @types packages, then npm update should typically just work.
It’s common for type declaration package updates to lag behind library updates because it’s often library users, not maintainers, who update Definitely Typed when new library features are released.
So, there may be a lag of days, weeks or even months before a helpful community member sends a PR to update the type declaration package for a new library release.
If you’re impacted by this, you can be the change you want to see in the world and you can be that helpful community member!
If you’re updating type declarations for a library, always set the major.minor version in package.json to match the library version that you’re documenting!
If a library is updated to a new major version with breaking changes, how should I update its type declaration package?
Semantic versioning requires that versions with breaking changes must increment the major version number.
For example, a library that removes a publicly exported function after its 3.5.8 release must bump its version to 4.0.0 in its next release.
Furthermore, when the library’s 4.0.0 release is out, it’s Definitely Typed type declaration package should also be updated to 4.0.0, including any breaking changes to the library’s API.
Many libraries have a large installed base of developers (including maintainers of other packages using that library as a dependency) who won’t move right away to a new version that has breaking changes, because it might be months until a maintainer has time to rewrite code to adapt to the new version.
In the meantime, users of old library versions still may want to update type declarations for older versions.
If you intend to continue updating the older version of a library’s type declarations, you may create a new subfolder (e.g. /v2/) named for the current (soon to be “old”) version and copy existing files from the current version to it.
When creating a new version folder, ensure that the version field of package.json has been updated; pnpm will automatically resolve to this versioned package whenever it’s needed. If other packages in the repo need to depend on this new version, ensure that their package.jsons are also updated too.
For example, if we are creating types/history/v2, its package.json would look like:
Also, /// <reference types=".." /> will not work with path mapping, so dependencies must use import.
How do breaking type changes work if type declaration packages closely track the library package’s version?
@types packages always type packages of the same version, so @types/foo@5.4.x are for foo@5.4.x.
As a consequence, all changes, breaking or not, are published as patch revisions, unless paired with a major/minor bump to change the package version being targeted (coincidentally or not).
When it comes to breaking changes, DT maintainers consider the popularity of the package, the upsides of the proposed breaking change, the effort that will be required for users to fix their code, and whether the change could reasonably be delayed until it can be synced with a major bump of the upstream library.
How do I write definitions for packages that can be used globally and as a module?
The TypeScript handbook contains excellent general information about writing definitions and also this example definition file which shows how to create a definition using ES6-style module syntax, while also specifying objects made available to the global scope. This technique is demonstrated practically in the definition for big.js, which is a library that can be loaded globally via script tag on a web page or imported via require or ES6-style imports.
To test how your definition can be used both when referenced globally or as an imported module, create a test folder and place two test files in there. Name one YourLibraryName-global.test.ts and the other YourLibraryName-module.test.ts. The global test file should exercise the definition according to how it would be used in a script loaded on a web page where the library is available on the global scope - in this scenario you should not specify an import statement. The module test file should exercise the definition according to how it would be used when imported (including the import statement(s)). If you specify a files property in your tsconfig.json file, be sure to include both test files. A practical example of this is also available on the big.js definition.
Please note that it is not required to fully exercise the definition in each test file - it is sufficient to test only the globally accessible elements on the global test file and fully exercise the definition in the module test file or vice versa.
What about scoped packages?
Types for a scoped package @foo/bar should go in types/foo__bar. Note the double underscore.
License
This project is licensed under the MIT license.
Copyrights on the definition files are respective of each contributor listed at the beginning of each definition file.
Definitely Typed
You can also read this README in Español, 한국어, Русский, 简体中文, Português, Italiano, 日本語 and Français!
Link to Admin manual
!!! Important! This repo has recently changed layout! !!!
Definitely Typed has recently changed to a proper
pnpmmonorepo; you may want to reread this document for changes to the layout of packages in this repo.At the very least, you may want to
git clean -fdxthe repo (ornode ./scripts/clean-node-modules.json Windows) to clean upnode_modulesand runpnpm install --filter .to install the workspace root. See further sections for more info onpnpm install.Current status
This section tracks the health of the repository and publishing process. It may be helpful for contributors experiencing any issues with their PRs and packages.
If anything here seems wrong or any of the above are failing, please let us know in the Definitely Typed channel on the TypeScript Community Discord server.
What are declaration files and how do I get them?
See the TypeScript handbook.
npm
This is the preferred method. For example:
To install typings for a scoped module, remove the
@and add double-underscore after the scope. For example, to install typings for@babel/preset-env:The types should then be automatically included by the compiler. You may need to add a
typesreference if you’re not using modules:See more in the handbook.
For an npm package “foo”, typings for it will be at “@types/foo”.
If your package has typings specified using the
typesortypingskey in itspackage.json, the npm registry will display that the package has available bindings like so:If you still can’t find the typings, just look for any “.d.ts” files in the package and manually include them with a
/// <reference path="" />.Support Window
Definitely Typed only tests packages on versions of TypeScript that are less than 2 years old.
Older versions of TypeScript
@typespackages have tags for versions of TypeScript that they explicitly support, so you can usually get older versions of packages that predate the 2-year window. For example, if you runnpm dist-tags @types/react, you’ll see that TypeScript 2.5 can use types for react@16.0, whereas TypeScript 2.6 and 2.7 can use types for react@16.4:TypeScript 1.*
masterbranch of this repository and place them in your projectTypings(use preferred alternatives, typings is deprecated)NuGet(use preferred alternatives, nuget DT type publishing has been turned off)You may need to add manual references.
How can I contribute?
Definitely Typed only works because of contributions by users like you!
Testing
Before you share your improvement with the world, use the types yourself by creating a
typename.d.tsfile in your project and filling out its exports:Test editing an existing package
You can edit the types directly in
node_modules/@types/foo/index.d.tsto validate your changes, then bring the changes to this repo with the steps below.Alternatively, you can use module augmentation to extend existing types from the DT module or use the
declare moduletechnique above which will override the version innode_modules.Adding tests to a new package
Add to your
tsconfig.json:Create
types/foo/index.d.tscontaining declarations for the module “foo”. You should now be able to import from"foo"in your code and it will route to the new type definition. Then build and run the code to make sure your type definition actually corresponds to what happens at runtime.Once you’ve tested your definitions with real code, make a PR then follow the instructions to edit an existing package or create a new package.
Make a pull request
Once you’ve tested your package, you can share it on Definitely Typed.
--filter=blob:nonewhen runninggit clone.pnpm install.pnpm installwill install the entire repository, including packages you may not be editing. If you’d like to install only a subset, you can runpnpm install -w --filter "{./types/foo}..."to install@types/fooand all of its dependencies. If you need to run tests for packages that depend on@types/foo, you can runpnpm install -w --filter "...{./types/foo}..."to pull in all related packages for testing.We use a bot to let a large number of pull requests to DefinitelyTyped be handled entirely in a self-service manner. You can read more about why and how here. Here is a handy reference showing the life cycle of a pull request to DT:
Edit an existing package
pnpm test <package to test>.When you make a PR to edit an existing package,
dt-botshould @-mention the package’s owners. If it doesn’t, you can do so yourself in the comment associated with the PR.Create a new package
If you are the library author and your package is written in TypeScript, bundle the generated declaration files in your package instead of publishing to Definitely Typed. You can also generate declaration files from JavaScript files, using JSDoc for type annotations.
If you are adding typings for an npm package, create a directory with the same name. If the package you are adding typings for is not on npm, make sure the name you choose for it does not conflict with the name of a package on npm. (You can use
npm info <my-package>to check for the existence of the<my-package>package.)Your package should have this structure:
index.d.ts<my-package>-tests.tstsconfig.jsontscwithin the package..eslintrc.jsonpackage.json.npmignoreGenerate these by running
npx dts-gen --dt --name <my-package> --template module. See all options at dts-gen.If you have
.d.tsfiles besidesindex.d.ts, make sure that they are referenced either inindex.d.tsor the tests.Definitely Typed members routinely monitor for new PRs, though keep in mind that the number of other PRs may slow things down.
For a good example package, see base64-js.
Removing a package
When a package bundles its own types, types should be removed from Definitely Typed to avoid confusion.
You can remove it by running
pnpm run not-needed <typingsPackageName> <asOfVersion> [<libraryName>].<typingsPackageName>: This is the name of the directory to delete.<asOfVersion>: A stub will be published to@types/<typingsPackageName>with this version. Should be higher than any currently published version and should be a version of<libraryName>on npm.<libraryName>: Name of npm package that replaces the Definitely Typed types. Usually this is identical to<typingsPackageName>, in which case you can omit it.If a package was never on Definitely Typed, it does not need to be added to
notNeededPackages.json.Running tests
Test your changes by running
pnpm test <package to test>where<package to test>is the name of your package. You need to run this from the DefinitelyTyped directory because individual package.jsons don’t define test scripts.This script uses dtslint to run the TypeScript compiler against your dts files.
Once you have all your changes ready, use
pnpm run test-allto see how your changes affect other modules.@arethetypeswrong/cli (
attw) checksdtslint includes module format and
package.jsonconfiguration checks from @arethetypeswrong/cli. The checks run only if a SemVer-major-compatible implementation package can be found on npm to compare against the DefinitelyTyped package. (DefinitelyTyped packages marked asnonNpmin theirpackage.jsonare skipped.)Many packages currently fail the
attwchecks and need to be fixed. To allow us to make incremental progress, failedattwchecks do not fail thedtslintrun when the package is listed infailingPackagesinattw.json, but they will still be reported in thepnpm test my-packageoutput. If you fix the package, remove it fromfailingPackagesso thatattwchecks can start failingdtslintruns.All problems reported by
attwhave documentation linked in the output. Some rules of thumb to help avoid problems:The
package.jsonin the DefinitelyTyped package must have matchingtypeandexportsfields if the implementation package uses them in itspackage.json. For example, if an implementationpackage.jsonlooks like:then the DefinitelyTyped
package.jsonshould look something like:Notice that each
exportssubpath is reflected, and each JavaScript file has a corresponding declaration file with a matching file extension—a.d.tsfile types a.jsfile, not a.mjsor.cjsfile!When the implementation package uses
module.exports = ..., the DefinitelyTyped package should useexport =, notexport default. (Alternatively, if themodule.exportsis just an object of named properties, the DefinitelyTyped package can use a series of named exports.) The most common obstacle to correcting this problem is confusion about how to export types in addition to the primary export. For example, assume these types are incorrectly usingexport default:Changing the
export defaultto anexport =creates an error:To fix this, move the types inside a namespace with the same name as the function:
If you need help fixing a problem, please ask in the DefinitelyTyped channel on the TypeScript Community Discord server.
Naming
If you are adding typings for an npm package, create a directory with the same name. If the package you are adding typings for is not on npm, set
"nonNpm": truein thepackage.json, and make sure the name you choose for it does not conflict with the name of a package on npm. (You can usenpm info <my-package>to check for the existence of the<my-package>package.)In rare occasions,
nonNpmmay be set to"conflict", which incidates that there is a package on npm with the same name, but the types intentionally conflict with that package. This can be true for packages which define an environment like@types/nodeor for dummy packages likeaws-lambda. Avoid using"conflict"where possible.<my-package>-tests.tsThere should be a
<my-package>-tests.tsfile, which is considered your test file, along with any*.tsfiles it imports. If you don’t see any test files in the module’s folder, create a<my-package>-tests.ts. These files are used to validate the API exported from the*.d.tsfiles which are shipped as@types/<my-package>. They do not themselves ship.Changes to the
*.d.tsfiles should include a corresponding*.tsfile change which shows the API being used, so that someone doesn’t accidentally break code you depend on. For example, this change to a function in a.d.tsfile adding a new param to a function:index.d.ts:<my-package>-tests.ts:If you’re wondering where to start with test code, the examples in the README of the original package are a great place to start.
You can validate your changes with
npm test <package to test>from the root of this repo, which takes changed files into account.Use
$ExpectTypeto assert that an expression is of a given type and@ts-expect-errorto assert that a compile error. Examples:For more details, see dtslint readme.
Linter:
.eslintrc.jsonIf for some reason a lint rule needs to be disabled, disable it for a specific line:
You can still disable rules with an .eslintrc.json, but should not in new packages. Disabling rules for the entire package makes it harder to review.
tsconfig.jsontsconfig.jsonshould havenoImplicitAny,noImplicitThis,strictNullChecksandstrictFunctionTypesset totrue.You may edit the
tsconfig.jsonto add new test files, to add"target": "es6"(needed for async functions), to add to"lib"or to add the"jsx"compiler option.esModuleInterop/allowSyntheticDefaultImportsTL;DR:
esModuleInteropandallowSyntheticDefaultImportsare not allowed in yourtsconfig.json.package.jsonThis file is required and should follow this template:
A
package.jsonspecifies all dependencies, including other@typespackages.You must add non-
@typesdependencies to the list of allowed external dependencies. Pikaday is a good example. These additions are approved by a maintainer, which gives us the chance to make sure that@typespackages don’t depend on malicious packages.If the implementation package uses ESM and specifies
"type": "module", then you should modify package.json to match:This also applies if the implementation package has
exportsin its package.json.Peer dependencies
Definitely Typed allows
peerDependenciesinpackage.json. Peer dependencies can help prevent situations where a package manager unexpectedly installs too-new versions or more than one version of the same package. However, peer dependencies have downsides; package managers differ in their handling of peer dependencies (e.g.,yarndoes not auto-install them,npmrequires--legacy-peer-depsfor mismatches). As such, PRs introducing new peer dependencies require maintainer approval and should be limited to specific circumstances.In general, types packages should only have a peer dependency if the upstream package has a peer dependency on the same package (or its types). For example, a DT package for a React component can specify a peer dependency on
@types/react@*, as the consumer will have needed to install@types/reactto use JSX in the first place. If the consumer installs@types/react@16in their project, but a newer version of@types/reactis available on npm, the peer dependency may help the package manager choose@types/react@16instead of that newer version. Similarly,chai-as-promisedhas a peer dependency onchai, so@types/chai-as-promisedshould have a peer dependency on@types/chai.There are some cases where the upstream package does not have a peer dependency on the types package, but a peer dependency is still appropriate. These are typically cases where the upstream package extends another package and assumes it exists, so should have declared a peer dependency as it extends another package, but did not. For example,
chai-match-patternextendschai, but does not declare a peer dependency onchai, but needs it to function.@types/chai-match-patternshould have a peer dependency on@types/chai.If a package simply exposes types from another package as a part of its API due to a regular dependency in the upstream package, it should not use a peer dependency. For example,
expresshasqsin its"dependencies". When users installexpress, they don’t need to manually installqs. Likewise,@types/expresshas@types/qsin its"dependencies". It would be incorrect to declare@types/qsas a peer dependency of@types/express, since that would require some downstream consumers to manually install@types/qs..npmignoreThis file defines which files are to be included in each
@typespackage. It must take a specific form. For packages with only one version in the repo:Which is to say “ignore all files, but don’t ignore any declaration files”. For packages that have more than one version in the repo, the “latest” version (at the top level) should contain something like:
Which is the same as the previous
.npmignorebut ignoring each of the versioned child directories.CI will fail if this file contains the wrong contents and provide the intended value. No matter what this file contains, the publisher will only publish declaration files.
Common mistakes
pnpm dprint fmt -- 'path/to/package/**/*.ts'..vscode/settings.template.json(or equivalent for other editors) to format on save with the VS Code dprint extensionfunction sum(nums: number[]): number: UseReadonlyArrayif a function does not write to its parameters.interface Foo { new(): Foo; }: This defines a type of objects that are new-able. You probably wantdeclare class Foo { constructor(); }.const Class: { new(): IClass; }: Prefer to use a class declarationclass Class { constructor(); }instead of a new-able constant.getMeAT<T>(): T: If a type parameter does not appear in the types of any parameters, you don’t really have a generic function, you just have a disguised type assertion. Prefer to use a real type assertion, e.g.getMeAT() as number. Example where a type parameter is acceptable:function id<T>(value: T): T;. Example where it is not acceptable:function parseJson<T>(json: string): T;. Exception:new Map<string, number>()is OK.FunctionandObjectis almost never a good idea. In 99% of cases it’s possible to specify a more specific type. Examples are(x: number) => numberfor functions and{ x: number, y: number }for objects. If there is no certainty at all about the type,anyis the right choice, notObject. If the only known fact about the type is that it’s some object, use the typeobject, notObjector{ [key: string]: any }.var foo: string | any: Whenanyis used in a union type, the resulting type is stillany. So, while thestringportion of this type annotation may look useful, it in fact offers no additional typechecking over simply usingany. Depending on the intention, acceptable alternatives could beany,stringorstring | object.Definition owners
DT has the concept of “Definition Owners” which are people who want to maintain the quality of a particular module’s types.
To add yourself as a Definition Owner, modify the
ownersarray inpackage.json:Note that this list is not used to provide credit for contributions; it is only used for managing PR reviews.
Once a week the Definition Owners are synced to the file .github/CODEOWNERS which is our source of truth.
The history of Definitely Typed
Definitely Typed is one of the most active repositories on GitHub. You might have wondered how the project came to be. @johnnyreilly wrote a history of Definitely Typed. It tells the story of the early days of Definitely Typed, from a repository created by @borisyankov, to the point where it became a pivotal part of the TypeScript ecosystem. You can read the story of Definitely Typed here.
FAQ
What exactly is the relationship between this repository and the
@typespackages on npm?The
masterbranch is automatically published to the@typesscope on npm thanks to DefinitelyTyped-tools.I’ve submitted a pull request. How long until it is merged?
It depends, but most pull requests will be merged within a week. Some PRs can be merged by the owners of a module and they can be merged much faster. Roughly:
PRs that have been approved by an owner listed in the definition’s
package.jsonare usually merged more quickly; PRs for new definitions will take more time as they require more review from maintainers. Each PR is reviewed by a TypeScript or Definitely Typed team member before being merged, so please be patient as human factors may cause delays. Check the Pull Request Status Board to see progress as maintainers work through the open PRs.I’d like to submit a change to a very popular project, why are they treated differently?
For changes to very popular modules, e.g. Node/Express/Jest which have many millions of downloads each per week on npm, the requirements for contributions are a bit higher. Changes to these projects can have massive ecosystem effects and so we treat changes to them with a lot of care. These modules require both a sign-off from a DT maintainer and enthusiastic support from the module owners. The bar for passing this can be quite high and often PRs can go stale because it doesn’t have a champion. If you’re finding that no-one is committing, try to make your PR have a smaller focus.
My PR is merged; when will the
@typesnpm package be updated?npm packages should update within an hour. If it’s been more than an hour, mention the PR number on the Definitely Typed channel on the TypeScript Community Discord server and the current maintainer will get the correct team member to investigate.
I’m writing a definition that depends on another definition. Should I use
<reference types="" />or an import?If the module you’re referencing is a module (uses
export), use an import. If the module you’re referencing is an ambient module (usesdeclare module) or just declares globals, use<reference types="" />.Some packages have a
tsconfig.jsonthat is missing"noImplicitAny": true,"noImplicitThis": trueor"strictNullChecks": true.Then they are wrong and we’ve not noticed yet. You can help by submitting a pull request to fix them.
Are Files Formatted Automatically?
Yes, using dprint. We recommend using a dprint extension for your editor.
Alternatively, you can enable a git hook which will format your code automatically. Run
pnpm run setup-hooks. Then, when you commit,dprint fmtcommand will be executed on changed files.Pull requests do not require correct formatting to be merged. Any unformatted code will be automatically reformatted after being merged.
Can I request a definition?
Here are the currently requested definitions.
What about type definitions for the DOM?
If types are part of a web standard, they should be contributed to TypeScript-DOM-lib-generator so that they can become part of the default
lib.dom.d.ts.What about type definitions with no matching package?
If there’s no source JavaScript code at all, for example if you’re writing helper types or types for a spec, you should publish the types yourself, not on Definitely Typed. Because they’re meant to provide types for existing JavaScript code,
@typespackages are not meant to be imported directly. That is, you shouldn’t create a Definitely Typed package that’s meant to be used likeimport type { ... } from "@types/foo". Nor should you expect to writeimport type { ... } from "foo"when there’s nofooinstalled.This is different from providing types for a browser only JavaScript library or types for an entire environment like node, bun, et al. There, the types are either resolved implicitly or using
/// <references types="foo" />.Should I add an empty namespace to a package that doesn’t export a module to use ES6 style imports?
Some packages, like chai-http, export a function.
Importing this module with an ES6 style import in the form
import * as foo from "foo";leads to the error:This error can be suppressed by merging the function declaration with an empty namespace of the same name, but this practice is discouraged. This is a commonly cited Stack Overflow answer regarding this matter.
It is more appropriate to import the module using the
import foo = require("foo");syntax. Nevertheless, if you want to use a default import likeimport foo from "foo";you have two options:--allowSyntheticDefaultImportscompiler option if your module runtime supports an interop scheme for non-ECMAScript modules, i.e. if default imports work in your environment (e.g. Webpack, SystemJS, esm).--esModuleInteropcompiler option if you want TypeScript to take care of non-ECMAScript interop (since TypeScript 2.7).A package uses
export =, but I prefer to use default imports. Can I changeexport =toexport default?Like in the previous question, refer to using either the
--allowSyntheticDefaultImportsor--esModuleInteropcompiler options.Do not change the type definition if it is accurate. For an npm package,
export =is accurate ifnode -p 'require("foo")'works to import a module andexport defaultis accurate ifnode -p 'require("foo").default'works to import a module.I want to use features from very new TypeScript versions.
Then you will have set the minimum supported version by specifying
"minimumTypeScriptVersion": "X.Y"inpackage.json.However, if your project needs to maintain types that are compatible with, say, 3.7 and above at the same time as types that are compatible with 3.6 or below, you will need to use the
typesVersionsfeature. You can find a detailed explanation of this feature in the official TypeScript documentation.Here’s a short example to get you started:
You’ll have to add
typesVersionstopackage.json:Create the sub-directory mentioned in the
typesVersionsfield inside your types directory (ts3.6/in this example).ts3.6/will support TypeScript versions 3.6 and below, so copy the existing types and tests there.Back in the root of the package, add the TypeScript 3.7 features you want to use. When people install the package, TypeScript 3.6 and below will start from
ts3.6/index.d.ts, whereas TypeScript 3.7 and above will start fromindex.d.ts.You can look at bluebird for an example.
I want to add a DOM API not present in TypeScript by default.
This may belong in TypeScript-DOM-lib-generator. See the guidelines there. If the standard is still a draft, it belongs here. Use a name beginning with
dom-and include a link to the standard as the “Project” link inpackage.json. When it graduates draft mode, we may remove it from Definitely Typed and deprecate the associated@typespackage.How do Definitely Typed package versions relate to versions of the corresponding library?
NOTE: The discussion in this section assumes familiarity with Semantic versioning
Each Definitely Typed package is versioned when published to npm. The DefinitelyTyped-tools (the tool that publishes
@typespackages to npm) will set the declaration package’s version by using themajor.minor.9999version number listed inpackage.json. For example, here are the first few lines of Node’s type declarations for version20.8.xat the time of writing:Because the version is listed as
20.8.9999, the npm version of the@types/nodepackage will also be20.8.x. Note that the version inpackage.jsonshould only containmajor.minorversion (e.g.10.12) followed by.9999. This is because only the major and minor release numbers are aligned between library packages and type declaration packages. (The.9999is to ensure that local@typespackages are always newest during local development.) The patch release number of the type declaration package (e.g..0in20.8.0) is initialized to zero by Definitely Typed and is incremented each time a new@types/nodepackage is published to npm for the same major/minor version of the corresponding library.Sometimes type declaration package versions and library package versions can get out of sync. Below are a few common reasons why, in order of how much they inconvenience users of a library. Only the last case is typically problematic.
@typespackages, thennpm updateshould typically just work.major.minorversion inpackage.jsonto match the library version that you’re documenting!If a library is updated to a new major version with breaking changes, how should I update its type declaration package?
Semantic versioning requires that versions with breaking changes must increment the major version number. For example, a library that removes a publicly exported function after its
3.5.8release must bump its version to4.0.0in its next release. Furthermore, when the library’s4.0.0release is out, it’s Definitely Typed type declaration package should also be updated to4.0.0, including any breaking changes to the library’s API.Many libraries have a large installed base of developers (including maintainers of other packages using that library as a dependency) who won’t move right away to a new version that has breaking changes, because it might be months until a maintainer has time to rewrite code to adapt to the new version. In the meantime, users of old library versions still may want to update type declarations for older versions.
If you intend to continue updating the older version of a library’s type declarations, you may create a new subfolder (e.g.
/v2/) named for the current (soon to be “old”) version and copy existing files from the current version to it.When creating a new version folder, ensure that the version field of
package.jsonhas been updated;pnpmwill automatically resolve to this versioned package whenever it’s needed. If other packages in the repo need to depend on this new version, ensure that theirpackage.jsons are also updated too.For example, if we are creating
types/history/v2, itspackage.jsonwould look like:Another package may select this version by specifying:
Also,
/// <reference types=".." />will not work with path mapping, so dependencies must useimport.How do breaking type changes work if type declaration packages closely track the library package’s version?
@typespackages always type packages of the same version, so@types/foo@5.4.xare forfoo@5.4.x. As a consequence, all changes, breaking or not, are published as patch revisions, unless paired with a major/minor bump to change the package version being targeted (coincidentally or not).When it comes to breaking changes, DT maintainers consider the popularity of the package, the upsides of the proposed breaking change, the effort that will be required for users to fix their code, and whether the change could reasonably be delayed until it can be synced with a major bump of the upstream library.
How do I write definitions for packages that can be used globally and as a module?
The TypeScript handbook contains excellent general information about writing definitions and also this example definition file which shows how to create a definition using ES6-style module syntax, while also specifying objects made available to the global scope. This technique is demonstrated practically in the definition for
big.js, which is a library that can be loaded globally via script tag on a web page or imported via require or ES6-style imports.To test how your definition can be used both when referenced globally or as an imported module, create a
testfolder and place two test files in there. Name oneYourLibraryName-global.test.tsand the otherYourLibraryName-module.test.ts. The global test file should exercise the definition according to how it would be used in a script loaded on a web page where the library is available on the global scope - in this scenario you should not specify an import statement. The module test file should exercise the definition according to how it would be used when imported (including theimportstatement(s)). If you specify afilesproperty in yourtsconfig.jsonfile, be sure to include both test files. A practical example of this is also available on thebig.jsdefinition.Please note that it is not required to fully exercise the definition in each test file - it is sufficient to test only the globally accessible elements on the global test file and fully exercise the definition in the module test file or vice versa.
What about scoped packages?
Types for a scoped package
@foo/barshould go intypes/foo__bar. Note the double underscore.License
This project is licensed under the MIT license.
Copyrights on the definition files are respective of each contributor listed at the beginning of each definition file.