Search code examples
reactjstypescriptmaterial-uireact-proptypesunion-types

React Proptypes union type Breakpoint gives error


I am having trouble giving the right proptype to the material-ui Breakpoint type.

The breakpoint is as follows: export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl';

In my App.tsx if have the following code:

import React, { FC } from 'react'
import PropTypes from 'prop-types'
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints'
import withWidth from '@material-ui/core/withWidth'

interface IApp {
  width: Breakpoint
}

const App: FC<IApp> = ({ width }) => {
    // Code here
}

App.propTypes = {
  width: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']).isRequired,
}

export default withWidth()(App)

Which is giving me the following error:

Type '{ width: Validator<string>; }' is not assignable to type 'WeakValidationMap<IApp>'.
  Types of property 'width' are incompatible.
    Type 'Validator<string>' is not assignable to type 'Validator<Breakpoint>'.
      Type 'string' is not assignable to type 'Breakpoint'.ts(2322)

Solution

  • Problem

    When you do this:

    App.propTypes = {
      width: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']).isRequired,
    }
    

    TypeScript will treat ['xs', 'sm', 'md', 'lg', 'xl'] as an array of random strings, not the specific strings you are interested in.

    Solution (TypeScript 3.4+)

    To narrow their types down to the specific values defined by Breakpoint, use a const assertion.

    App.propTypes = {
      width: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl'] as const).isRequired,
    }
    

    Solution (TypeScript <3.4)

    If you're running a version of TypeScript older than 3.4, you can achieve the same result by creating an array of well-known string literals before you define your propTypes.

    const breakpoints: Breakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl'];
    
    App.propTypes = {
      width: PropTypes.oneOf(breakpoints).isRequired,
    }