How to Handle React PropTypes When Converting to TypeScript: A Migration Guide

Keep your existing propTypes definitions in place while adding TypeScript interfaces, then remove them gradually once your components are fully typed and tested.

When migrating a React application from JavaScript to TypeScript, developers face the challenge of reconciling runtime PropTypes validation with compile-time static typing. According to the facebook/react source code, the framework supports a gradual transition where both systems can coexist safely, allowing teams to adopt TypeScript without breaking existing development workflows or losing runtime safety nets.

Why React PropTypes and TypeScript Can Coexist During Migration

React’s core still actively reads propTypes from component definitions during development. In fixtures/legacy-jsx-runtimes/react-17/cjs/react-jsx-runtime.development.js at line 1070, the runtime explicitly checks type.propTypes to validate props before rendering. This means your existing runtime checks continue to function exactly as before, catching accidental misuse in development builds while TypeScript handles static analysis at compile time.

Step-by-Step Strategy for Handling React PropTypes in TypeScript

1. Preserve Existing PropTypes for Runtime Safety

Do not delete your propTypes definitions when you first add TypeScript. They provide immediate feedback in the browser console during development, which is valuable while you are still converting dependent components. The React repository’s own migration approach demonstrates that propTypes remain functional even as the codebase adopts stricter typing.

2. Add Parallel TypeScript Interfaces

Create an interface or type alias that mirrors your propTypes shape, then annotate the component’s props parameter. This gives you compile-time validation without removing the runtime safety net.

import PropTypes from 'prop-types';
import type {FC} from 'react';

interface ButtonProps {
  label: string;
  onClick?: () => void;
}

const Button: FC<ButtonProps> = ({label, onClick}) => (
  <button onClick={onClick}>{label}</button>
);

// Runtime checks remain for legacy environments
Button.propTypes = {
  label: PropTypes.string.isRequired,
  onClick: PropTypes.func,
};

export default Button;

3. Configure ESLint to Allow Both Systems

The React repository disables the react/prop-types rule in its own .eslintrc.js at lines 188–190 to prevent the linter from flagging the temporary coexistence of both type systems. Apply the same configuration to your project to eliminate false positives while you transition.

// .eslintrc.js
module.exports = {
  rules: {
    'react/prop-types': 'off',
  },
};

4. Gradually Remove PropTypes After TypeScript Validation

Once a component is fully typed and covered by tests, delete its propTypes definition. The official React CHANGELOG.md at line 192 notes that propTypes will eventually be ignored and recommends moving to TypeScript or another static-type solution. This phased removal prevents regressions while cleaning up the codebase.

import type {FC} from 'react';

interface ButtonProps {
  label: string;
  onClick?: () => void;
}

const Button: FC<ButtonProps> = ({label, onClick}) => (
  <button onClick={onClick}>{label}</button>
);

export default Button;

Handling Edge Cases and Legacy Code

Managing React.PropTypes Imports

If your legacy code uses React.PropTypes, you must migrate to the external prop-types package. The React CHANGELOG.md entry for version 15.2.1 at line 1521 explains that React.PropTypes was moved to the separate prop-types package. Additionally, scripts/error-codes/codes.json entry 84 contains the runtime error message triggered when React.PropTypes is accessed directly, confirming this separation.

// Before (deprecated)
import React from 'react';
React.PropTypes.string;

// After (correct)
import PropTypes from 'prop-types';
PropTypes.string;

Suppressing Duplicate Type Warnings

If TypeScript complains about duplicate type definitions when both systems are present, use a suppression comment until you remove the propTypes line.

// @ts-ignore – temporary until PropTypes removal
Button.propTypes = {
  label: PropTypes.string.isRequired,
};

Key Files in the React Source Code Supporting This Pattern

The facebook/react repository contains several files that demonstrate official support for this migration pattern:

  • fixtures/legacy-jsx-runtimes/react-17/cjs/react-jsx-runtime.development.js (line 1070) – Contains the runtime check that reads type.propTypes during component rendering, proving that PropTypes remain functional in modern React.

  • CHANGELOG.md (line 192) – Documents the recommendation to migrate from PropTypes to TypeScript or other static type solutions, and notes that PropTypes will eventually be ignored.

  • .eslintrc.js (lines 188–190) – Shows the official React repository configuration that disables the react/prop-types ESLint rule, allowing both type systems to coexist during migration.

  • packages/react/src/__tests__/testDefinitions/PropTypes.d.ts – Provides a minimal TypeScript shim that allows the React test suite to compile while still importing from the 'prop-types' package.

  • scripts/error-codes/codes.json (entry 84) – Contains the error message for deprecated React.PropTypes usage, confirming the separation of the PropTypes package from the core React library.

Summary

  • Keep propTypes in place when first adding TypeScript to maintain runtime validation during development, as React’s core still checks type.propTypes at runtime.
  • Add parallel TypeScript interfaces to enforce compile-time contracts without removing the safety net that catches errors in the browser console.
  • Disable the react/prop-types ESLint rule to prevent false positives while both systems coexist, following the pattern used in the React repository’s own .eslintrc.js.
  • Migrate React.PropTypes imports to the external prop-types package to avoid runtime errors, as the core React library no longer exports PropTypes directly.
  • Remove propTypes gradually only after components are fully typed and tested, cleaning up the codebase once TypeScript provides complete static coverage.

Frequently Asked Questions

Should I remove React PropTypes immediately when adding TypeScript?

No, you should keep your existing propTypes definitions in place during the initial migration. They provide immediate runtime feedback in development builds while you are still converting dependent components and refining your TypeScript interfaces. Remove them only after a component is fully typed and covered by tests.

Do PropTypes still run in development when using TypeScript?

Yes, React’s development build continues to validate propTypes at runtime regardless of whether you are using TypeScript. In fixtures/legacy-jsx-runtimes/react-17/cjs/react-jsx-runtime.development.js at line 1070, the React core explicitly checks type.propTypes during the rendering process, ensuring your runtime validations still catch errors in the browser console.

How do I stop ESLint from complaining about missing PropTypes in TypeScript files?

Disable the react/prop-types rule in your ESLint configuration. The React repository itself uses this approach in .eslintrc.js at lines 188–190, setting 'react/prop-types': 'off' to allow both TypeScript interfaces and propTypes definitions to coexist without linting errors during the migration period.

Can I use both PropTypes and TypeScript interfaces permanently?

While technically possible, it is not recommended for production codebases. The React CHANGELOG.md at line 192 indicates that propTypes will eventually be ignored in future releases and recommends moving to TypeScript or another static-type solution. Maintaining both systems long-term creates duplication, increases bundle sizes, and adds unnecessary maintenance overhead.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →