diff --git a/.changeset/new-kiwis-destroy.md b/.changeset/new-kiwis-destroy.md new file mode 100644 index 00000000..3d317512 --- /dev/null +++ b/.changeset/new-kiwis-destroy.md @@ -0,0 +1,5 @@ +--- +"types-react-codemod": patch +--- + +Document usage of CLI and each transform diff --git a/README.md b/README.md index b37e1055..4bb0860b 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,41 @@ Collection of transforms for [jscodeshift](https://github.com/facebook/jscodeshift) related to `@types/react`. +## Getting started + +The codemod helps to fix potential TypeScript compile errors when upgrading to `@types/react@^18.0.0`. +However, we recommend to apply this codemod if you're using `@types/react@^17.0.30`. + +```bash +$ npx types-react-codemod preset-18 ./src +? Pick transforms to apply (Press to select, to toggle all, to invert selection, and to proce +ed) +❯◯ context-any + ◉ deprecated-react-type + ◉ deprecated-sfc-element + ◉ deprecated-sfc + ◉ deprecated-stateless-component + ◯ implicit-children + ◯ useCallback-implicit-any +All done. +Results: +0 errors +20 unmodified +0 skipped +3 ok +Time elapsed: 0.229seconds +``` + ## Usage ```bash $ npx types-react-codemod --help types-react-codemod -default - Positionals: codemod [string] [required] [choices: "context-any", "deprecated-react-type", "deprecated-sfc-element", "deprecated-sfc", "deprecated-stateless-component", - "implicit-children", "useCallback-implicit-any"] + "implicit-children", "preset-18", "useCallback-implicit-any"] paths [string] [required] Options: @@ -36,6 +59,7 @@ Fixing all of these requires a lot of implementation effort. When considering false-positives vs false-negatives, I opt for false-positives. The reason being that a false-positive can be reverted easily (assuming you use have the changed code in Version Control e.g. git) while a false-negative requires manual input. +- `preset-18` - `deprecated-react-type` - `deprecated-sfc-element` - `deprecated-sfc` @@ -44,8 +68,25 @@ The reason being that a false-positive can be reverted easily (assuming you use - `implicit-children` - `useCallback-implicit-any` +### `preset-18` + +This codemod combines all codemods for React 18 types. +You can interactively pick the codemods included. +By default, we the codemods that are definitely required to upgrade to `@types/react@^18.0.0` are selected. +The other codemods may or may not be required. +You should select all and audit the changed files regardless. + ### `context-any` +```diff + class Component extends React.Component { ++ context: any + render() { + return this.context.someContextProperty; + } + } +``` + You should only apply this codemod to files where the type-checker complains about access of `unknown` in `this.context`. We'll check for any occurence of `context` (case-sensitive) in a `React.Component` body (or `React.PureComponent`). If we find any occurence of `context` we'll add `context: any` declaration to the class body. @@ -90,12 +131,30 @@ I just think that most class components do not use `this.context` (or already ha ### All `deprecated-` transforms +```diff +-React.ReactType ++React.ElementType +-React.SFC ++React.FC +-React.StatelessComponent ++React.FunctionComponent +-React.SFCElement ++React.FunctionComponentElement +``` + They simply rename identifiers with a specific name. If you have a type with the same name from a different package, then the rename results in a false positive. For example, `ink` also has a `StatelessComponent` but you don't need to rename that type since it's not deprecated. ### `implicit-children` +```diff +-React.FunctionComponent ++React.FunctionComponent> +-React.FunctionComponent ++React.FunctionComponent> +``` + This transform will wrap the props type of `React.FunctionComponent` (and `FC`, `SFC` and `StatelessComponent`) with `React.PropsWithChildrne`. Note, that the transform assumes `React.PropsWithChildren` is available. We can't add that import since `React.PropsWithChildren` can be available via `tsconfig.json`. @@ -113,3 +172,24 @@ Redundant `PropsWithChildren` are only problematic stylistically. `MyFunctionComponent` where `MyFunctionComponent` comes from `import { FunctionComponent as MyFunctionComponent } from 'react'` will be ignored. In other words, the transform will not wrap `Props` in `React.PropsWithChildren`. The transform would need to implement scope tracking for this pattern to get fixed. + +### `useCallback-implicit-any` + +```diff +-React.useCallback((event) => {}) ++React.useCallback((event: any) => {}) +``` + +This transform should only be applied to files where TypeScript errors with "Parameter '\*' implicitly has an 'any' type.(7006)" in `useCallback`. + +#### `useCallback-implicit-any` false-positive pattern A + +If the callback param is inferrable by TypeScript we might apply `any` without need. +In the example below the type of `event` is inferrable and adding `any` essentially reduces type coverage. +This is why it's recommended to only apply `useCallback-implicit-any` to files that produce "Parameter '\*' implicitly has an 'any' type.(7006)" when type-checking with `@types/react@^18.0.0`. + +```diff +type CreateCallback = () => (event: Event) => void; +-const createCallback: CreateCallback = () => useCallback((event) => {}, []) ++const createCallback: CreateCallback = () => useCallback((event: any) => {}, []) +``` diff --git a/transforms/useCallback-implicit-any.js b/transforms/useCallback-implicit-any.js index fd29c244..943c9a8c 100644 --- a/transforms/useCallback-implicit-any.js +++ b/transforms/useCallback-implicit-any.js @@ -8,10 +8,6 @@ const parseSync = require("./utils/parseSync"); * 23154 Files unmodified * 4790 skipped * 99 Ok (6 of which are false-positive but quickly identified) - * - * False-Positive Pattern #1 - * `const foo: Type = () => useCallback(event => {})` - * BUT this gets increasingly complicated if this becomes `const foo = () => useCallback(event => {})` */ const useCallbackImplicitAnyTransform = (file, api) => { const j = api.jscodeshift;