Skip to content
Mig ration F low
language typescript react proptypes types

PropTypes TypeScript

Replace runtime PropTypes validation with compile-time TypeScript interfaces.

Copy for

Using an AI agent? See /agents for MCP, JSON, and llms.txt access.

PropTypes → TypeScript

Philosophy shift

PropTypes validate props at runtime in development — errors show up in the console after the component renders. TypeScript catches mismatches at compile time, before the code runs. Once on TypeScript, PropTypes are redundant.

Rule: Remove propTypes and defaultProps declarations after adding TypeScript types. Keeping both creates a maintenance burden with no added safety.

Setup

If not already on TypeScript, see the JavaScript → TypeScript playbook first. Then:

npm install --save-dev @types/react @types/react-dom
npm uninstall prop-types

Core transformations

Basic props

// PropTypes
import PropTypes from 'prop-types';

function Button({ label, onClick, disabled }) {
  return <button onClick={onClick} disabled={disabled}>{label}</button>;
}

Button.propTypes = {
  label: PropTypes.string.isRequired,
  onClick: PropTypes.func.isRequired,
  disabled: PropTypes.bool,
};

Button.defaultProps = {
  disabled: false,
};

// TypeScript
interface ButtonProps {
  label: string;
  onClick: () => void;
  disabled?: boolean;
}

function Button({ label, onClick, disabled = false }: ButtonProps) {
  return <button onClick={onClick} disabled={disabled}>{label}</button>;
}

Enum / oneOf

// PropTypes
Component.propTypes = {
  variant: PropTypes.oneOf(['primary', 'secondary', 'danger']),
};

// TypeScript
interface Props {
  variant?: 'primary' | 'secondary' | 'danger';
}

Shape / nested objects

// PropTypes
Component.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string.isRequired,
    name: PropTypes.string.isRequired,
    email: PropTypes.string,
  }).isRequired,
};

// TypeScript
interface User {
  id: string;
  name: string;
  email?: string;
}

interface Props {
  user: User;
}

Arrays

// PropTypes
Component.propTypes = {
  items: PropTypes.arrayOf(PropTypes.string).isRequired,
  users: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string })),
};

// TypeScript
interface Props {
  items: string[];
  users?: Array<{ id: string }>;
}

Children

// PropTypes
Component.propTypes = {
  children: PropTypes.node.isRequired,
  child: PropTypes.element.isRequired,
};

// TypeScript
interface Props {
  children: React.ReactNode;
  child: React.ReactElement;
}

Functions with signatures

// PropTypes
Component.propTypes = {
  onChange: PropTypes.func.isRequired,
  onSubmit: PropTypes.func,
};

// TypeScript — be explicit about the signature
interface Props {
  onChange: (value: string) => void;
  onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void;
}

instanceOf / custom validators

// PropTypes
Component.propTypes = {
  date: PropTypes.instanceOf(Date),
  custom: PropTypes.custom, // custom validator function
};

// TypeScript
interface Props {
  date?: Date;
  custom?: string; // replace with the actual type
}

defaultProps → default parameters

// PropTypes defaultProps
Component.defaultProps = {
  size: 'medium',
  disabled: false,
};

// TypeScript — default values in destructuring
function Component({ size = 'medium', disabled = false }: Props) { ... }

PropTypes → TypeScript type mapping

PropTypesTypeScript
stringstring
numbernumber
boolboolean
func() => void (specify signature)
arrayunknown[] (specify element type)
objectRecord<string, unknown> (define interface)
nodeReact.ReactNode
elementReact.ReactElement
symbolsymbol
anyunknown (avoid any)
oneOf([...])union type 'a' | 'b'
oneOfType([...])union type string | number
arrayOf(T)T[]
shape({...})interface or inline object type
instanceOf(C)InstanceType<typeof C>
.isRequiredremove ? (required by default)
absent (optional)add ? to property

When NOT to migrate

Pitfalls

Validation checklist

Codemod references

AI Prompt

You are migrating a React component from PropTypes to TypeScript.

Rules:
1. Create an interface (or type) for the component's props, named <ComponentName>Props.
2. Map each propTypes entry to its TypeScript equivalent using this table:
   - string → string, number → number, bool → boolean
   - func → explicit function signature (e.g. () => void or (val: string) => void)
   - node → React.ReactNode, element → React.ReactElement
   - oneOf([...]) → union type ('a' | 'b')
   - shape({...}) → inline object type or named interface
   - arrayOf(T) → T[]
   - .isRequired → required prop (no ?); absent → optional prop (?)
3. Replace defaultProps with default parameter values in the function signature.
4. Remove the propTypes and defaultProps declarations entirely.
5. Do not change JSX or component logic.

Migrate the following component:

References