Repository: TypeScript
Issue: noThirdPartyAny to flag anys that come from third-party type declarations
Labels: Suggestion,Awaiting More Feedback

# Suggestion

## 🔍 Search Terms

- noImplicitAny @types

Related:
- https://github.com/microsoft/TypeScript/issues/33886
- https://github.com/microsoft/TypeScript/issues/27265
- https://github.com/microsoft/TypeScript/issues/26188

## ✅ Viability Checklist

<!--
   Suggestions that don't meet all these criteria are very, very unlikely to be accepted.
   We always recommend reviewing the TypeScript design goals before investing time writing
   a proposal for ideas outside the scope of the project.
-->
My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code (it would have to an opt-in via a new flag)
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).


## ⭐ Suggestion

I'd like to add a new flag, perhaps `noThirdPartyAny`, that makes it an error for a symbol to have an `any` type _if that `any` comes from third-party code_, e.g. from `@types` or from `lib/dom.d.ts`. (Just like with `noImplicitAny`, it would be fine if you wanted to explicitly give that symbol an `any`.)

## 📃 Motivating Example

<!--
  If you were announcing this feature in a blog post, what's a short explanation that shows
  a developer why this feature improves the language?
-->

TypeScript with `noImplicitAny` requires you to provide type annotations on all symbols that would otherwise implicitly have an `any` type. You'd think that this would only leave you with _explicit_ `any`s, i.e. a symbol can only have an `any` type if you write the word `any` somewhere.

But that's not quite true because type declarations files (`@types`) can include `any` types as well.

Maybe the most common example is `JSON.parse` (also `Response.prototype.json()`):

```ts
const data = JSON.parse('{"a": 12}');
const b = data.b.toString();  // 💥
```

I'm using `--strict` and I never wrote `any`, but the type of `b` is still `any` and it leads to a runtime error that TS misses.

Here's a more intricate React example that tripped me up this week:

```ts
import React from 'react';

function MyComponent() {
  const [value, setValue] = React.useState('');
  return (<input onChange={e => setValue(e.currentTarget.value)} value={value} />);
}
```

This works correctly. The `e` parameter in the callback has a type of `React.ChangeEvent<HTMLInputElement>`, which is exactly right.

Now I decide to memoize the handler using `React.useCallback`, copy-pasting my callback:

```ts
import React from 'react';

function MyComponent() {
  const [value, setValue] = React.useState('');
  const handleChange = React.useCallback(e => setValue(e.currentTarget.value), []);
  return (<input onChange={handleChange} value={value} />);
}
```

Again, this works (at runtime) and it passes the type checker. But it's actually a looming disaster because `e` now has an `any` type! This comes from the React typings:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/07e9b3d9d301b7d17390c16816c02cdf92706ecb/types/react/index.d.ts#L1099-L1107

This use of `any` is a known issue (https://github.com/DefinitelyTyped/DefinitelyTyped/issues/52873) that the maintainers prefer not to fix until the next major version of React comes out. That's fine, but it makes `useCallback` very `any`-prone.

I've found that `any`s that come from `@types` and the built-in libraries are quite common and always surprising. They let `any`s creep into your code even when you think you're being strict. I'd like some way to have TypeScript help me fight back against these implicit `any`s. 

## 💻 Use Cases

<!--
  What do you want to use this for?
  What shortcomings exist with current approaches?
  What workarounds are you using in the meantime?
-->

See above. `noImplicitAny` solves this for `any`s that are truly implicit. I'm proposing a new flag that would extend this to `any` symbols that do appear explicitly in type declarations, just not ones that I wrote.

You can use declaration merging to eliminate these on a case-by-case basis (see [blog post](https://effectivetypescript.com/2021/06/03/interface/)) but this feels like playing whack-a-mole. The new `--useUnknownInCatchVariables` flag is another tool in this game of `any` whack-a-mole.

The best way to spot these `any` leaks is with ts-coverage (https://github.com/plantain-00/type-coverage), which reports every symbol in your code that has an `any` type, wherever it came from. The downside is that this exists outside the TypeScript toolchain, so I tend to only run it every once in a while, whereas type errors always get fixed immediately.