Coder Social home page Coder Social logo

types-react-codemod's Introduction

types-react-codemod

Collection of transforms for 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.

$ npx types-react-codemod preset-18 ./src
? Pick transforms to apply (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> 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

$ npx types-react-codemod <codemod> <paths...>

Positionals:
  codemod [string] [required] [choices: "context-any", "deprecated-react-child",
                     "deprecated-react-fragment", "deprecated-react-node-array",
     "deprecated-react-text", "deprecated-react-type", "deprecated-sfc-element",
                             "deprecated-sfc", "deprecated-stateless-component",
         "deprecated-void-function-component", "implicit-children", "preset-18",
    "preset-19", "refobject-defaults", "scoped-jsx", "useCallback-implicit-any",
                                                      "useRef-required-initial"]
  paths                                                      [string] [required]

Options:
  --version         Show version number                                [boolean]
  --help            Show help                                          [boolean]
  --dry                                               [boolean] [default: false]
  --ignore-pattern                      [string] [default: "**/node_modules/**"]
  --verbose                                           [boolean] [default: false]

Examples:
  types-react-codemod preset-18 ./          Ignores `node_modules` and `build`
  --ignore-pattern                          folders
  "**/{node_modules,build}/**"

Available transforms

Some transforms change code they shouldn't actually change. Fixing all of these requires a lot of implementation effort. When considering false-positives vs false-negatives, codemods opt for false-positives. The reason being that a false-positive can be reverted easily (assuming you 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
  • deprecated-stateless-component
  • context-any
  • implicit-children
  • useCallback-implicit-any
  • preset-19
  • deprecated-react-child
  • deprecated-react-text
  • deprecated-void-function-component
  • refobject-defaults
  • scoped-jsx
  • useRef-required-initial

preset-18

This codemod combines all codemods for React 18 types. You can interactively pick the codemods included. By default, 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

 class Component extends React.Component<Props> {
+  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.

false-positive on context usage

We'll add context: any even if you write const { context } = props. This simplifies the implementation tremendously and follows the overall rationale for false-positives: it can be reverted easily and at worst restores the behavior of React 17 typings.

false-negative when inheriting from another component

Class inheritance chains are not handled.

class A extends React.Component {}

class B extends A {
	render() {
		// will error since the transform does not add `context: any` to the declaration of `A` nor `B`.
		// It's up to you to decide whether `A` or `B` should have this declaration
		return this.context.value;
	}
}

We'll also miss usage of context if it's accessed outside of the class body e.g.

function getValue(that) {
	return that.context.value;
}

class A extends React.Component {
	render() {
		return getValue(this);
	}
}

This doesn't really follow the general transform rationale of "over-applying" since at worst we restore React 17 behavior. I just think that most class components do not use this.context (or already have a type declaration) somewhere else.

All deprecated- transforms

-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

-React.FunctionComponent<Props>
+React.FunctionComponent<React.PropsWithChildren<Props>>
-React.FunctionComponent
+React.FunctionComponent<React.PropsWithChildren<unknown>>

This transform will wrap the props type of React.FunctionComponent (and FC, ComponentType, SFC and StatelessComponent) with React.PropsWithChildren. Note, that the transform assumes React.PropsWithChildren is available. We can't add that import since React.PropsWithChildren can be available via tsconfig.json.

implicit-children false-positive pattern A

We'll apply React.PropsWithChildren everytime. If you have a component that doesn't actually take children, we'll not fix what removal of implicit children should've fixed.

Similarly, if your props already have children declared, PropsWithChildren will be redundant. Redundant PropsWithChildren are only problematic stylistically.

implicit-children false-positive pattern B

MyFunctionComponent<Props> 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

-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.

type CreateCallback = () => (event: Event) => void;
-const createCallback: CreateCallback = () => useCallback((event) => {}, [])
+const createCallback: CreateCallback = () => useCallback((event: any) => {}, [])

preset-19

This codemod combines all codemods for React 19 types. You can interactively pick the codemods included. By default, the codemods that are definitely required to upgrade to @types/react@^19.0.0 are selected. The other codemods may or may not be required. You should select all and audit the changed files regardless.

deprecated-react-child

 import * as React from "react";
 interface Props {
-  label?: React.ReactChild;
+  label?: React.ReactElement | number | string;
 }

deprecated-react-child false-negative pattern A

Importing ReactChild via aliased named import will result in the transform being skipped.

import { ReactChild as MyReactChild } from "react";
interface Props {
	// not transformed
	label?: MyReactChild;
}

deprecated-react-node-array

 import * as React from "react";
 interface Props {
-  children?: React.ReactNodeArray;
+  children?: ReadonlyArray<React.ReactNode>;
 }

deprecated-react-node-array false-negative pattern A

Importing ReactNodeArray via aliased named import will result in the transform being skipped.

import { ReactNodeArray as MyReactNodeArray } from "react";
interface Props {
	// not transformed
	children?: MyReactNodeArray;
}

deprecated-react-fragment

 import * as React from "react";
 interface Props {
-  children?: React.ReactFragment;
+  children?: Iterable<React.ReactNode>;
 }

deprecated-react-fragment false-negative pattern A

Importing ReactFragment via aliased named import will result in the transform being skipped.

import { ReactFragment as MyReactFragment } from "react";
interface Props {
	// not transformed
	children?: MyReactFragment;
}

deprecated-react-text

 import * as React from "react";
 interface Props {
-  label?: React.ReactText;
+  label?: number | string;
 }

deprecated-react-text false-negative pattern A

Importing ReactText via aliased named import will result in the transform being skipped.

import { ReactText as MyReactText } from "react";
interface Props {
	// not transformed
	label?: MyReactText;
}

deprecated-void-function-component

WARNING: Only apply to codebases using @types/react@^18.0.0. In earlier versions of @types/react this codemod would change the typings.

 import * as React from "react";
-const Component: React.VFC = () => {}
+const Component: React.FC = () => {}
-const Component: React.VoidFunctionComponent = () => {}
+const Component: React.FunctionComponent = () => {}

refobject-defaults

WARNING: This is an experimental codemod to intended for codebases using unpublished types. Only use if you're using DefinitelyTyped/DefinitelyTyped#64896.

RefObject no longer makes current nullable by default

 import * as React from "react";
-const myRef: React.RefObject<View>
+const myRef: React.RefObject<View | null>

experimental-refobject-defaults false-negative pattern A

Importing RefObject via aliased named import will result in the transform being skipped.

import { RefObject as MyRefObject } from "react";

// not transformed
const myRef: MyRefObject<View>;

scoped-jsx

Ensures access to global JSX namespace is now scoped to React (see DefinitelyTyped/DefinitelyTyped#64464). This codemod tries to match the existing import style but isn't perfect. If the import style doesn't match your preferences, you should set up auto-fixable lint rules to match this e.g. import/order.

+import { JSX } from 'react'
-const element: JSX.Element = <div />;
+const element: JSX.Element = <div />;
 import * as React from 'react';
-const element: JSX.Element = <div />;
+const element: React.JSX.Element = <div />;

useRef-required-initial

useRef now always requires an initial value. Implicit undefined is forbidden.

 import * as React from "react";
-React.useRef()
+React.useRef(undefined)

useRef-required-initial false-negative pattern A

Importing useRef via aliased named import will result in the transform being skipped.

import { useRef as useReactRef } from "react";

// not transformed
useReactRef<number>();

Supported platforms

The following list contains officially supported runtimes. Please file an issue for runtimes that are not included in this list.

  • Node.js 16.x || 18.x || 20.x

types-react-codemod's People

Contributors

dgarciamuria avatar eps1lon avatar github-actions[bot] avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

types-react-codemod's Issues

Transformation error: crashes on TSInstantiationExpression

When I run the code on one of my codebases, it crashed with the error:

Transformation error (did not recognize object of type "TSInstantiationExpression")
Error: did not recognize object of type "TSInstantiationExpression"

I think it's related to a new TS feature like Map<string, Error>:

function makeBox<T>(value: T) {
    return { value };
};

const makeStringBox = makeBox<string>; // <- This is a "TSInstantiationExpression"
const stringBox = makeStringBox('abc');

const ErrorMap = Map<string, Error>; // <- This is a "TSInstantiationExpression"
const errorMap = new ErrorMap();

See the following code on astexplorer.net (view snippet) with JS and recast (jscodeshift's engine):
image

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Edited/Blocked

These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/node.js.yml
  • actions/checkout v4
  • actions/setup-node v4
.github/workflows/release.yml
  • actions/checkout v4
  • actions/setup-node v4
  • changesets/action v1
npm
package.json
  • @babel/core ^7.17.8
  • @babel/parser ^7.17.8
  • @babel/preset-env ^7.16.11
  • inquirer ^9.0.0
  • jscodeshift ^0.15.0
  • yargs ^17.4.0
  • @changesets/changelog-github ^0.5.0
  • @changesets/cli ^2.22.0
  • @jest/globals ^29.0.0
  • @types/inquirer ^9.0.0
  • @types/jscodeshift ^0.11.3
  • @types/node ^20.0.0
  • @types/yargs ^17.0.10
  • dedent ^1.0.0
  • eslint ^8.12.0
  • jest ^29.0.0
  • prettier ^3.0.0
  • prettier-v2 ^2.8.8
  • typescript ^5.0.0
  • node 18.x || 20.x
  • node 20.11.0
  • yarn 3.7.0

  • Check this box to trigger a request for Renovate to run again on this repository

React 19 types codemods

Breaking changes: DefinitelyTyped/DefinitelyTyped#64451

  • #318
  • Unflag all codemods #319
  • refs are mutable by default: refobject-defaults
  • Require initial value for useRef: useref-required-initial (#217)
  • Remove deprecated global JSX namespace: scoped-jsx
  • Remove deprecated SFCFactory (not planned)
  • Remove deprecated ReactText
  • Remove deprecated ReactChild
  • Remove deprecated ReactNodeArray (low priority): deprecated-react-node-array (#325)
  • Remove deprecated ReactFragment (low priority): deprecated-react-fragment (#326)
  • Remove deprecated VFC: deprecated-void-function-component
  • Remove deprecated VoidFunctionComponent: deprecated-void-function-component
  • Remove deprecated ReactChildren (low priority)
  • Remove deprecated Mixin (not planned)
  • Remove deprecated ComponentSpec (not planned)

Issue with types on React.Children.map()

interface ChildrenProps {
 children: React.ReactNode;
}
const Component = ({ children}: ChildrenProps) => {
 {React.Children.map(children, (child: React.ReactElement, i) => {
 }

I'm getting an error with children inside React.Children.map() when i use React.ReactNode

Argument of type 'ReactNode' is not assignable to parameter of type 'ReactElement<any, string | JSXElementConstructor<any>> | readonly ReactElement<any, string | JSXElementConstructor<any>>[]'.ts(2345)

but when I use children: JSX.Element[] | JSX.Element; as type, it doesn't. I'm still new to typescript and react and I couldn't find any answers as I search perhaps my keywords are wrong but if you guys are free can you help me understand why JSX. The element would work but not ReactNode? Anything can be assigned with React Node right? but I can't assign ReactNode to ReactElement?

Document recommended usage

  1. Updatges @types/react to v18
  2. type-check your repo
  3. run codemod
  4. revert codemod changes to files that didn't cause issues in initial type-check

Defective implicit children codemod

Sebastian,

The implicit children codemod has the following defects . Any workarounds you can suggest ?

  • We have a repo with 1000's of components . The codemod adds children to all components whether it should really have children or not . We have only 10% of the components in our repo with children and 90% without any children.

  • Our codebase still heavily uses class components whereas the codemod works only for functional components

got ERR_REQUIRE_ESM on implicit-children

Hi,
I've got a "ERR_REQUIRE_ESM" error, trying to run implicit-children codemod

Output:

C:\Users\dorki\WebstormProjects\reversio-front>npx types-react-codemod implicit-children .\packages/@savgroup-front-common/ui/src/atoms/AddAndRemove/AddAndRemove.tsx --dry
executing "C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules.bin\jscodeshift --extensions=tsx,ts "--ignore-pattern=/node_modules/" --transform C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\types-react-codemod\transforms\implicit-children.js --dry .\packages/@savgroup-front-common/ui/src/atoms/AddAndRemove/AddAndRemove.tsx"
Processing 1 files...
Spawning 1 workers...
Running in dry mode, no files will be written!
Sending 1 files to free worker...
Browserslist: caniuse-lite is outdated. Please run the following command: yarn upgrade
C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\types-react-codemod\transforms\implicit-children.js:141
undefined
^

Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\dorki\WebstormProjects\reversio-front\node_modules\babel-preset-react-app\node_modules@babel\runtime\helpers\esm\toConsumableArray.js from C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\types-react-codemod\transforms\implicit-children.js not supported.
Instead change the require of toConsumableArray.js in C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\types-react-codemod\transforms\implicit-children.js to a dynamic import() which is available in all CommonJS modules.
at Object.newLoader [as .js] (C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\pirates\lib\index.js:141:7)
at Object. (C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\types-react-codemod\transforms\implicit-children.js:10:50)
at Module._compile (C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\pirates\lib\index.js:136:24)
at Object.newLoader [as .js] (C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\pirates\lib\index.js:141:7)
at setup (C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\jscodeshift\src\Worker.js:91:18)
at Object. (C:\Users\dorki\AppData\Local\npm-cache_npx\07333c78fb78394f\node_modules\jscodeshift\src\Worker.js:45:3) {
code: 'ERR_REQUIRE_ESM'
}

Node.js v18.2.0
All done.
Results:
0 errors
0 unmodified
0 skipped
0 ok
Time elapsed: 5.345seconds

Back to React.FunctionalComponent?

I jumped on the bandwagon to abandon React.FC, but now they have dropped the implicit children issue, so I feel that React.FunctionalComponent is the best way to type a React function. This mod must be easy for those who know this no? I want to go from:

export const Hello = ({
  treeID = 'testTreeID',
  counter = 0,
}: HelloProps): ReactElement => {
  return (
      <HelloCmp />
  );
};

to

export const Hello = FunctionalComponent<HelloProps> = 

and remove the :HelloProps from the props, and the return type of ReactElement and add:

import { type FunctionalComponent } from 'react';

Import error

I received the following error when running the codemod:

/Users/..../..../node_modules/types-react-codemod/transforms/preset-18.js:1
import contextAnyTransform from "./context-any";
^^^^^^

SyntaxError: Cannot use import statement outside a module

Command run was npx types-react-codemod preset-18 ./src

This is using nodejs 16.

FYI, Downgrading to 1.1.0 resolved it. Maybe related to #63 since the import in question is preset-18

Implicit children error not working

I tried to disable that error using react with typescript and it doesn't fixed that error
versions:

"@types/react": "^17.0.30",
"react": "^18.1.0",

Implement `context-any`

Implementation suggestion 1:
Add context: any to the class declaration if we find access to this.context within the class body

Add preset for v18

  • deprecated types are on by default
  • prompt for enabling other codemods ordered by likelyhood of false-positives

Implement `implicit-children`

Implementation suggestion 1:

Wrap type parameter of FunctionComponent (FC, SFC, StatelessComponent so that we guard against codemods for deprecated types being applied after this codemod) with PropsWithChildren.
If no type parameter is found add PropsWithChildren<{}> as type parameter.

Adding extraenous children is not considered a false-positive since type-coverage remains the same. Checking additions may be helpful to understand why implicit children were problematic.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.