# typeshed: the type annotations Python never shipped

> A collection of stub files for the standard library and for third-party packages that ship no types of their own. Understanding the version numbers takes more care than installing them does.

**python/typeshed** — Collection of library stubs for Python, with static types

- Repository: https://github.com/python/typeshed
- Stars: 5,128 · Forks: 2,104
- Language: Python
- License: NOASSERTION
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/python-typeshed

## Stubs for code that has no annotations at all

The About section is short enough to quote in full: typeshed contains external type annotations for the Python standard library and Python builtins, as well as third-party packages that are contributed by people external to those projects. The phrase that matters is external. These annotations are not upstream. They are written by volunteers who read the source of `requests` or `html5lib` and describe its shape in a separate file.

A stub file is a description rather than an implementation. It declares modules, classes, functions and their signatures with no function bodies, so a type checker can read it and infer what your code should look like without ever importing the real package. That is the entire trick, and it is why a library written in C or in JavaScript can participate fully in a type checked Python program.

The data is consumed by static analysis, type checking, type inference and autocompletion. Note the ordering of that list. Typeshed is not a runtime library and never gets imported by your application; it is a dataset that tools read ahead of time.

GitHub reports 5124 stars, 2093 forks and 356 open issues for this repository. The fork count is high relative to the star count, which tells you something real about the shape of the work: a large share of contributors are not using typeshed so much as maintaining a stub for one package in it.

## You almost certainly never clone this repository

The first thing the README tells a user is that if you are only using a type checker such as mypy, pyright or PyCharm's built in checker, you do not need to interact with this repository at all. A copy of the standard library half of typeshed is bundled with type checkers, so that part is already on your machine.

Third-party stubs are distributed separately on PyPI under the `types-` prefix. If you use `html5lib` and `requests`, one command covers both:

```bash
$ pip install types-html5lib types-requests
```

Those PyPI packages follow the typing spec standards and are released automatically by typeshed internal machinery, up to once a day. So the workflow for an application developer is pip install and nothing else. There is no build step, no vendored directory and no path configuration.

The repository's tree confirms the split cleanly: `stdlib/` for the standard library, `stubs/` for third-party packages distributed one directory per project, and `lib/` for the shared support code that the tooling itself consumes. Running `ls stubs/` is the fastest way to find out whether the package you care about has stubs at all.

## Four version components, and why the last one is a date

The section on package versioning is the most practically useful documentation in the README, and it is unusual to find it explained anywhere else. Stub package versions carry at least four parts. Every part except the last corresponds to the version of the runtime package being stubbed.

The worked example in the README: if the `types-foo` package has version `1.2.0.20240309`, that guarantees the package contains stubs targeted against `foo==1.2.*` and tested against the latest `foo` matching that specifier. The final element, 20240309, is the push date, which in this case is March 9, 2024.

```text
1.2.0.20240309
```

That design resolves a genuine problem. A stub package has to say two different things at once, which version of the library it describes and when it was last refreshed, and a conventional semantic version cannot carry the second. By burying the date in the final component, pip can match stubs to your dependency constraints using the first three components while still telling you how fresh the annotations are.

The README also warns that although typeshed tries to keep breaking changes to a minimum, any version bump can introduce changes that might make your code fail to type check. Stubs are more volatile than runtime libraries for a simple reason: adding a more precise type to a function that was previously `Any` can turn code that type checked yesterday into code that does not today.

## Three pinning strategies with honest tradeoffs

Because any bump can change results, the README spells out three ways to specify the stub version. The first is to mirror the bounds you use for the runtime package, so `requests>=2.30.0,<2.32` becomes `types-requests>=2.30.0,<2.32`. The tradeoff is that stubs often lag the package they describe, and if you force a minimum version to pick up a critical bug fix but matching stubs have not shipped yet, your type results are quietly wrong.

The second is to pin to a known good version and update deliberately, perhaps with dependabot or renovate, for example `types-requests==2.31.0.1`. This gives confidence that a dependency bump will not suddenly break checking, at the price of missing stub improvements until you update, and of risking incompatibility with a runtime package that has since moved on.

The third is not to pin at all. It is the least work and you pick up improvements automatically, but a new major version of the runtime package can land stubs that describe it before you upgrade the package itself.

The README explicitly permits mixing, for example defaulting to strategy one and falling back to a pin when something breaks. For most teams this is the correct answer, and the specific detail to remember is that the third component of the version is what pip compares against your constraints.

## Utility types that exist only for type checkers

One feature deserves its own mention because its name sends people looking for it in the wrong place. typeshed includes a package named `_typeshed` as part of its standard library. The README states that it and its submodules contain utility types but are not available at runtime, and points to the `stdlib/_typeshed` directory.

The leading underscore is doing real work there. Because the package does not exist at runtime, importing it in application code will raise `ImportError`. It exists only for annotation positions, where it helps express types that the standard library does not otherwise describe, such as precise parameter or return shapes that ordinary builtins cannot capture. This is also why a search for that module name on PyPI or in your own site-packages finds nothing, which is a frequent source of confusion.

The tooling configuration in the tree tells you how seriously the project takes consistency across checkers. Alongside `pyproject.toml` there are separate `pyrightconfig.json` files for the main run, for scripts and tests, for a stricter pass and for test cases, plus `ty.toml` and `pyrefly.toml` for two other type checkers. Running one standard linter over all of it would be simpler, and the number of config files is evidence that the same annotations have to satisfy several independent checkers with different ideas about what is correct.

## Where bugs about annotations should be filed

The README is unusually direct about this, and it is the rule that trips up newcomers most often: do not report issues with annotations to the project the stubs are for, report them to typeshed instead. If `requests` behaves in a way its stub does not describe, the fix belongs here, because the maintainers of `requests` did not write the stub and generally do not accept patches to it.

Discussion has three homes. The GitHub issue tracker is the main forum and the right place to start for anything about the project. General questions about typing in Python, or a request to review annotations outside typeshed, belong on the python/typing discussions forum. The typing chat room on gitter.im is for less formal conversation, and the README notes that some maintainers are almost always present there, while substantive technical discussion is redirected to the issue tracker.

The repository reports no releases, which is correct rather than a gap: the artifacts here are the PyPI `types-` packages and the copy of the standard library stubs bundled inside checkers, not tagged versions of this repository. Typeshed supports Python 3.10 through 3.14, and GitHub reports the last push on 2026-09-22 on a repository that is not archived. For a reader deciding whether to adopt stubs, the practical test is simpler than any of this: run your type checker on one untyped dependency, install its `types-` package, and see whether the error count drops.

## Conclusion

typeshed is the quiet reason a Python codebase with no types at all can still be type checked in full, and the packaging format it invented, the types- prefix with a pinned calendar suffix, is now the standard other stub repositories follow. What the repository does not settle is whether a stub package is accurate for your exact dependency range, which is why the README spends more words on three pinning strategies than on anything else. Install the stubs that cover your untyped dependencies, read the version suffix before you pin, and file the bug in typeshed rather than upstream when an annotation is wrong.

## FAQ

### What are Python stubs?

They are files that declare modules, classes and function signatures with type annotations but no implementation, so a type checker can read them without importing the real package. typeshed is the collection of such files for the Python standard library and for third-party packages that do not ship their own.

### Do I need to clone the typeshed repository to use its annotations?

No. A copy of the standard library part of typeshed is bundled with type checkers such as mypy, pyright and PyCharm, and third-party stubs are installed from PyPI with pip, for example pip install types-html5lib types-requests.

### What does the four part version of a types- package mean?

The first three components correspond to the version of the runtime package being stubbed, so 1.2.0.20240309 targets foo==1.2.*, and the final component is the date the stub package was pushed. The README notes stubs often lag the package they describe.

### Can I import _typeshed at runtime?

No. It contains utility types that are not available at runtime, and the README points to the stdlib/_typeshed directory for how to use it. It exists for annotation positions, so importing it in application code will fail.

### Where do I report a stub that does not match the library it describes?

File it on the typeshed GitHub issue tracker rather than with the library's own project. The README states plainly that issues with annotations should not be reported to the project the stubs are for.

## Sources

- [Issues](https://github.com/python/typeshed/issues)
- [python/typeshed on GitHub](https://github.com/python/typeshed)
- [README](https://github.com/python/typeshed/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/python-typeshed
