Repository: Sage/carbon (ts)

Issue:
Invalid prop warnings coming from style system props.
### Current behaviour

Now getting lots of console warnings regarding prop validation from prop types defined by third-party library.

### Expected behaviour

gridColumn prop on GridItem component should accept string value

### CodeSandbox or Storybook URL

https://codesandbox.io/s/style-prop-types-not-being-parsed-by-ts-ps90bb?file=/src/index.js

### JIRA Ticket (Sage Only)

_No response_

### Suggested Solution

Check TS pasring.

### Carbon Version

111.8.4

### Design Tokens Version

_No response_

### What browsers are you seeing the problem on?

Chrome

### What Operating System are you seeing the problem on?

Windows

### Anything else we should know?

Discussed on slack first.

### Confidentiality

- [X] I confirm there is no confidential or commercially sensitive information included.

Pull request description:
feat(packages): remove `typescript-to-proptypes` package
Resolves #5603 

### Proposed behaviour

Remove `typescript-to-proptypes` package

### Current behaviour

The `typescript-to-proptypes` package uses an older version of TS that will not support the new jsx compiler option that React 19 onwards will require us to use.

### Checklist

- [x] Commits follow our style guide
- [x] Related issues linked in commit messages if required
- [ ] Screenshots are included in the PR if useful
- [ ] All themes are supported if required
- [ ] Unit tests added or updated if required
- [ ] Playwright automation tests added or updated if required
- [ ] Storybook added or updated if required
- [ ] Translations added or updated (including creating or amending translation keys table in storybook) if required
- [ ] Typescript `d.ts` file added or updated if required
- [ ] Related docs have been updated if required

#### QA

- [ ] Tested in provided StackBlitz sandbox/Storybook
- [ ] Add new Playwright test coverage if required
- [ ] Carbon implementation matches Design System/designs
- [ ] UI Tests GitHub check reviewed if required

### Additional information

The following outlines the steps followed to completely remove the `typescript-to-proptypes` package from the repository. This will allow the codebase to be upgraded to React 19.

1. Removed the `typescript-to-proptypes` package
2. Removed all references, scripts and configurations related to it:
	1. Removed the `plugins` directory as all files within were no longer relevant
	2. Updated the `babel.config.js` to turn off prop type generation in production.
3. Ensured that no components continue to rely on `PropType` definitions
4. Turned the `react/prop-types` ESLint rule `off`:
	1. Was previously set to `warn`
5. Ran the TypeScript type checking script to ensure there are no type errors in the codebase via `npm run type-check`:
	1. No typing violations raised
6. Ran the linter to ensure there are no linting errors via `npm run lint`:
	1. No errors found
7. Ran the unit tests via `npm run test` to ensure that all tests continue to operate as expected
	1. All tests passed
	2. 100% coverage maintained
8. Ran the Playwright test suite to ensure removal has no impact on E2E tests
	1. Tests ran successfully (12 flaky tests, passed at second time of asking)
9. Ran the general build via `npm run build`
	1. No issues reported
10. Ran a manual check of all components
	1. No issues found

### Testing instructions

There should be no visual or functional regressions as a result of this PR; all unit and E2E tests passed locally. However, it would be remiss not to run a manual check in Storybook to ensure nothing has broken as a result of the update

Patch:
diff --git a/.eslintrc b/.eslintrc
index 093082eaa..0372ee282 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -54,7 +54,7 @@
     "react/forbid-prop-types": "off",
     "react/no-array-index-key": "warn",
     "react/no-unused-prop-types": "warn",
-    "react/prop-types": "warn",
+    "react/prop-types": "off",
     "react/require-default-props": "off",
     "react/sort-comp": [
       "error",
@@ -232,7 +232,7 @@
         "playwright": {
           "messages": {
             "noSkippedTest": "Test skipped. If unresolved, raise a JIRA ticket or GitHub issue, then reference it in a code comment above.",
-            "noWaitForTimeout": "Hardcoded timeouts make tests fragile and inefficient. Use Playwright’s built-in waits (e.g. `locator.waitFor()`) or auto-retrying assertions (e.g. `await expect(locator).toBeVisible()`) to wait for conditions like element visibility or text presence." 
+            "noWaitForTimeout": "Hardcoded timeouts make tests fragile and inefficient. Use Playwright’s built-in waits (e.g. `locator.waitFor()`) or auto-retrying assertions (e.g. `await expect(locator).toBeVisible()`) to wait for conditions like element visibility or text presence."
           }
         }
       }
diff --git a/babel.config.js b/babel.config.js
index 502c2f6f2..a80387dcf 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -1,6 +1,3 @@
-const inProduction = process.env.NODE_ENV === "production";
-const inStorybookBuild = process.env.STORYBOOK_BUILD === "true";
-
 module.exports = (api) => {
   const useCJSModules = api.env(["test", "cjs"]);
 
@@ -22,9 +19,6 @@ module.exports = (api) => {
       "@babel/preset-typescript",
     ],
     plugins: [
-      !inStorybookBuild &&
-        inProduction &&
-        "./plugins/generate-proptypes/index.mjs",
       "@babel/plugin-proposal-class-properties",
       "@babel/plugin-transform-classes",
       "babel-plugin-dev-expression",
diff --git a/docs/usage.mdx b/docs/usage.mdx
index 50e0b5526..ded4aff81 100644
--- a/docs/usage.mdx
+++ b/docs/usage.mdx
@@ -12,6 +12,7 @@ import { Meta } from "@storybook/blocks";
   - [Theming](#theming)
   - [Localisation](#localisation)
   - [Validation](#validation)
+  - [A Note On PropTypes](#a-note-on-proptypes)
 
 ## Quick start
 
@@ -83,4 +84,20 @@ We also provide a `i18nProvider` global for handling different locales. For more
 
 Carbon provides built-in validation states for input components. For more information check our [Validations](../?path=/docs/documentation-validations--docs) documentation page.
 
-To opt into the new validation pattern, set the `validationRedesignOptIn` flag to true in the [CarbonProvider](../?path=/docs/carbon-provider--docs).
\ No newline at end of file
+To opt into the new validation pattern, set the `validationRedesignOptIn` flag to true in the [CarbonProvider](../?path=/docs/carbon-provider--docs).
+
+### A Note On PropTypes
+
+We recently removed support for `PropTypes` in Carbon components. This was done as a prerequisite of upgrading to React 19; it also encourages the use of TypeScript over JavaScript. If you are still using JavaScript, we recommend migrating your project to TypeScript to take advantage of the benefits it provides.
+
+If you need type checking in your JavaScript project, you can add a `.tsconfig` file to your project and set the `checkJs` flag to `true`. This will enable type checking for JavaScript files in your project.
+
+```json
+{
+  "compilerOptions": {
+    "checkJs": true
+  }
+}
+```
+
+More information on the `checkJs` TSConfig option can be found [here](https://www.typescriptlang.org/tsconfig/#checkJs). For further information on migrating to TypeScript, see the [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html).
diff --git a/package-lock.json b/package-lock.json
index 9c3b73494..47ee3f5d7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -45,7 +45,6 @@
         "@babel/preset-env": "^7.23.3",
         "@babel/preset-react": "^7.23.3",
         "@babel/preset-typescript": "^7.23.3",
-        "@babel/template": "^7.22.15",
         "@babel/types": "^7.23.4",
         "@chromatic-com/storybook": "^2.0.2",
         "@commitlint/cli": "^17.6.3",
@@ -145,7 +144,6 @@
         "storybook": "^8.4.7",
         "styled-components": "^4.4.1",
         "typescript": "~4.7.0",
-        "typescript-to-proptypes": "^2.2.1",
         "url-loader": "^4.1.1",
         "uuid": "^8.3.2",
         "webpack-dev-server": "^4.15.1"
@@ -28681,45 +28679,6 @@
         "node": ">=4.2.0"
       }
     },
-    "node_modules/typescript-to-proptypes": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/typescript-to-proptypes/-/typescript-to-proptypes-2.2.1.tgz",
-      "integrity": "sha512-FxVo0Rcf/c6dmHxA1DlAmpxct+1SuuDyX3Rl8MkfQt//yvZCMhWOuWZvJ3aP0/5eZTYIb+DpPt7htTL6A1xK9A==",
-      "dev": true,
-      "dependencies": {
-        "@babel/core": "^7.11.1",
-        "@babel/plugin-syntax-class-properties": "^7.10.4",
-        "@babel/plugin-syntax-jsx": "^7.10.4",
-        "@babel/types": "^7.11.0",
-        "doctrine": "^3.0.0",
-        "lodash": "^4.17.14",
-        "tslib": "^1.13.0",
-        "typescript": "3.8.3",
-        "uuid": "^8.1.0"
-      },
-      "engines": {
-        "node": ">=10.3.0"
-      }
-    },
-    "node_modules/typescript-to-proptypes/node_modules/tslib": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
-      "dev": true
-    },
-    "node_modules/typescript-to-proptypes/node_modules/typescript": {
-      "version": "3.8.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
-      "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
-      "dev": true,
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=4.2.0"
-      }
-    },
     "node_modules/uglify-js": {
       "version": "3.19.3",
       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
diff --git a/package.json b/package.json
index 2b01af0a9..bea891d41 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,6 @@
     "@babel/preset-env": "^7.23.3",
     "@babel/preset-react": "^7.23.3",
     "@babel/preset-typescript": "^7.23.3",
-    "@babel/template": "^7.22.15",
     "@babel/types": "^7.23.4",
     "@chromatic-com/storybook": "^2.0.2",
     "@commitlint/cli": "^17.6.3",
@@ -160,7 +159,6 @@
     "storybook": "^8.4.7",
     "styled-components": "^4.4.1",
     "typescript": "~4.7.0",
-    "typescript-to-proptypes": "^2.2.1",
     "url-loader": "^4.1.1",
     "uuid": "^8.3.2",
     "webpack-dev-server": "^4.15.1"
diff --git a/plugins/generate-proptypes/index.mjs b/plugins/generate-proptypes/index.mjs
deleted file mode 100644
index f3e415544..000000000
--- a/plugins/generate-proptypes/index.mjs
+++ /dev/null
@@ -1,84 +0,0 @@
-import * as tsUtils from "./ts-utils.mjs";
-import injectPropTypes from "./inject-prop-types.mjs";
-
-/**
- * Custom Babel plugin that injects `import PropTypes from "prop-types";`
- * and js proptypes in the format `Component.propTypes = {...};` into
- * TypeScript components just before they are compiled to JavaScript.
- */
-
-export default async function ({ types: t }) {
-  const tsProgram = await tsUtils.generateTsProgram();
-
-  function inject(path, props, nodeName) {
-    injectPropTypes(t, {
-      path,
-      props,
-      nodeName,
-    });
-  }
-
-  return {
-    visitor: {
-      ImportDeclaration(path, state) {
-        const { filename } = state;
-        if (filename.includes(".component.tsx")) {
-          // Find React import and insert prop types after
-          if (t.isStringLiteral(path.node.source, { value: "react" })) {
-            path.insertAfter(
-              t.importDeclaration(
-                [t.importDefaultSpecifier(t.identifier("PropTypes"))],
-                t.stringLiteral("prop-types")
-              )
-            );
-          }
-        }
-      },
-      FunctionDeclaration(path, state) {
-        const { filename } = state;
-        if (filename.includes(".component.tsx")) {
-          const { node } = path;
-          if (!node.id) return;
-
-          const proptypesAST = tsUtils.generateProptypes(filename, tsProgram);
-          const nodeName = node.id.name;
-          const props = proptypesAST.body.find(
-            (prop) => prop.name === nodeName
-          );
-          if (!props) return;
-
-          inject(path, props, nodeName);
-        }
-      },
-      VariableDeclarator(path, state) {
-        const { filename } = state;
-        if (filename.includes(".component.tsx")) {
-          const { node } = path;
-          if (!t.isIdentifier(node.id)) return;
-
-          const proptypesAST = tsUtils.generateProptypes(filename, tsProgram);
-          const nodeName = node.id.name;
-          const props = proptypesAST.body.find(
-            (prop) => prop.name === nodeName
-          );
-          if (!props) return;
-
-          if (
-            t.isArrowFunctionExpression(node.init) ||
-            t.isFunctionExpression(node.init)
-          ) {
-            inject(path.parentPath, props, nodeName);
-          } else if (t.isCallExpression(node.init)) {
-            const arg = node.init.arguments[0];
-            if (
-              t.isArrowFunctionExpression(arg) ||
-              t.isFunctionExpression(arg)
-            ) {
-              inject(path.parentPath, props, nodeName);
-            }
-          }
-        }
-      },
-    },
-  };
-}
diff --git a/plugins/generate-proptypes/inject-prop-types.mjs b/plugins/generate-proptypes/inject-prop-types.mjs
deleted file mode 100644
index f50acac41..000000000
--- a/plugins/generate-proptypes/inject-prop-types.mjs
+++ /dev/null
@@ -1,57 +0,0 @@
-import template from "@babel/template";
-import * as ttp from "typescript-to-proptypes";
-
-/**
- * Create `Component.propTypes = {...}` source and inject it into the
- * AST of the current file.
- * Wraps in an `if (process.env.NODE_ENV !== "production")` to ensure proptypes are not generated
- * in production builds - as these are not needed in production and for some components (eg Box) lead
- * to a big increase in bundle size.
- * @param t Babel types object
- * @param options path, AST of props, and name of node to inject proptypes for
- */
-
-function injectPropTypes(t, options) {
-  const { path: nodePath, props, nodeName } = options;
-  const propTypesSource = ttp.generate(props, {
-    importedName: "PropTypes",
-  });
-
-  const conditionalPropTypes = `if (process.env.NODE_ENV !== "production") {${propTypesSource}}`;
-
-  if (propTypesSource.length === 0) {
-    return;
-  }
-
-  // Check if there's already "Node.propTypes = ..." for this node
-  let existingProptypes = false;
-  const programNode = nodePath.findParent((path) => path.isProgram());
-  const propTypesVisitor = {
-    MemberExpression(path) {
-      if (
-        t.isIdentifier(path.node.object, { name: nodeName }) &&
-        t.isIdentifier(path.node.property, { name: "propTypes" })
-      ) {
-        existingProptypes = true;
-        path.stop();
-      }
-    },
-  };
-  programNode.traverse(propTypesVisitor);
-
-  if (!existingProptypes) {
-    if (t.isExportNamedDeclaration(nodePath.parent)) {
-      nodePath.insertAfter(template.smart.ast(`export { ${nodeName} };`));
-      nodePath.insertAfter(template.smart.ast(conditionalPropTypes));
-      nodePath.parentPath.replaceWith(nodePath.node);
-    } else if (t.isExportDefaultDeclaration(nodePath.parent)) {
-      nodePath.insertAfter(template.smart.ast(`export default ${nodeName};`));
-      nodePath.insertAfter(template.smart.ast(conditionalPropTypes));
-      nodePath.parentPath.replaceWith(nodePath.node);
-    } else {
-      nodePath.insertAfter(template.smart.ast(conditionalPropTypes));
-    }
-  }
-}
-
-export default injectPropTypes;
diff --git a/plugins/generate-proptypes/ts-utils.mjs b/plugins/generate-proptypes/ts-utils.mjs
deleted file mode 100644
index 62bb57b29..000000000
--- a/plugins/generate-proptypes/ts-utils.mjs
+++ /dev/null
@@ -1,30 +0,0 @@
-import * as pathModule from "path";
-import fg from "fast-glob";
-import * as ttp from "typescript-to-proptypes";
-
-/**
- * Generate a TypeScript program object for all the ts files in src
- */
-async function generateTsProgram() {
-  const tsconfig = ttp.loadConfig(
-    pathModule.resolve(pathModule.resolve(), "./tsconfig.json")
-  );
-
-  const tsFiles = await fg("**/*.{ts,tsx}", {
-    absolute: true,
-    cwd: pathModule.resolve(pathModule.resolve(), "./src"),
-  });
-
-  return ttp.createProgram(tsFiles, tsconfig);
-}
-
-/**
- * Generate a PropTypes AST from the TypeScript interfaces in the current file
- * @param filename File to get the prop types from
- * @param program TypeScript program object
- */
-function generateProptypes(filename, program) {
-  return ttp.parseFromProgram(filename, program);
-}
-
-export { generateTsProgram, generateProptypes };
diff --git a/src/components/button/button.component.tsx b/src/components/button/button.component.tsx
index bfb05eae7..8c12c3a3b 100644
--- a/src/components/button/button.component.tsx
+++ b/src/components/button/button.component.tsx
@@ -112,7 +112,6 @@ interface RenderChildrenProps
 }
 
 function renderChildren({
-  /* eslint-disable react/prop-types */
   iconType,
   iconPosition,
   size,
@@ -123,8 +122,7 @@ function renderChildren({
   iconTooltipMessage,
   iconTooltipPosition,
   tooltipTarget,
-}: /* eslint-enable */
-RenderChildrenProps) {
+}: RenderChildrenProps) {
   const iconColor = () => {
     if (buttonType === "primary") {
       return "--colorsActionMajorYang100";


Test patch:
diff --git a/src/components/action-popover/action-popover-test.stories.tsx b/src/components/action-popover/action-popover-test.stories.tsx
index da66d0ca6..c8a85c74f 100644
--- a/src/components/action-popover/action-popover-test.stories.tsx
+++ b/src/components/action-popover/action-popover-test.stories.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/prop-types */
 import React from "react";
 import { action } from "@storybook/addon-actions";
 
diff --git a/src/components/action-popover/components.test-pw.tsx b/src/components/action-popover/components.test-pw.tsx
index f4be66452..a65cf8b4d 100644
--- a/src/components/action-popover/components.test-pw.tsx
+++ b/src/components/action-popover/components.test-pw.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/prop-types */
 import React, { useState } from "react";
 import Box from "../box";
 import Link from "../link";
diff --git a/src/components/alert/alert-test.stories.tsx b/src/components/alert/alert-test.stories.tsx
index 886936edc..e11d77de6 100644
--- a/src/components/alert/alert-test.stories.tsx
+++ b/src/components/alert/alert-test.stories.tsx
@@ -86,7 +86,6 @@ DefaultStory.story = {
 };
 
 export const AlertComponentTest = ({
-  // eslint-disable-next-line react/prop-types
   children = "This is an example of an alert",
   ...props
 }) => {
diff --git a/src/components/content/components.test-pw.tsx b/src/components/content/components.test-pw.tsx
index e983beb6d..737688833 100644
--- a/src/components/content/components.test-pw.tsx
+++ b/src/components/content/components.test-pw.tsx
@@ -2,7 +2,6 @@ import React from "react";
 import Content from ".";
 
 const ContentComponentTest = ({
-  // eslint-disable-next-line react/prop-types
   children = "This is an example of some content",
   ...props
 }) => {
diff --git a/src/components/textarea/textarea-test.stories.tsx b/src/components/textarea/textarea-test.stories.tsx
index 899c06c99..904003755 100644
--- a/src/components/textarea/textarea-test.stories.tsx
+++ b/src/components/textarea/textarea-test.stories.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/prop-types */
 import React, { useState } from "react";
 import { StoryObj } from "@storybook/react/*";
 import Textarea, { TextareaProps } from ".";
diff --git a/src/components/tooltip/tooltip-test.stories.tsx b/src/components/tooltip/tooltip-test.stories.tsx
index c32fe8768..4c4c1d811 100644
--- a/src/components/tooltip/tooltip-test.stories.tsx
+++ b/src/components/tooltip/tooltip-test.stories.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/prop-types */
 import React, { forwardRef, useState } from "react";
 import Tooltip from ".";
 import { TOOLTIP_POSITIONS, TooltipPositions } from "./tooltip.config";