Search code examples
reactjstypescriptnext.js

Can't use merged types in React props


I create the following 2 types in theme.interface.ts:

type ThemeSizing =
  | 'px'
  | '1'
  | '1/2'
  | 'full'
  | 'fit'

type ThemeWidthSpecific = 'svw' | 'lvw' | 'dvw'
type ThemeHeightSpecific = 'svh' | 'lvh' | 'dvh'

I create the merged types:

export type ThemeWidth = ThemeSizing & ThemeWidthSpecific
export type ThemeHeight = ThemeSizing & ThemeHeightSpecific

I declare the interface for the component in list.component.tsx:

interface ListProps {
  items: ReactNode[]
  width?: ThemeWidth
}
Error code in list.component.tsx:

TS2322: Type string is not assignable to type never

Error code in page.tsx (imported list component here):

TS2322: Type string is not assignable to type undefined list.component.tsx(22, 3): The expected type comes from property width which is declared here on type IntrinsicAttributes & ListProps

Perhaps I have to make the 2 created types optional. Couldn't find any solutions on the web unfortunately.


Solution

  • I think you misunderstood union and intersection types.

    Imagine the union to be an "or" statement. In you're case:

    type ThemeSizing =
      | 'px'
      | '1'
      | '1/2'
      | 'full'
      | 'fit'
    

    Means that ThemeSizing can be 'px' or '1' or '1/2' ....

    The same also goes for type ThemeWidthSpecific = 'svw' | 'lvw' | 'dvw'

    Now an intersection using & means that you're using an "and" so this code here:

    export type ThemeWidth = ThemeSizing & ThemeWidthSpecific
    

    means that ThemeWidth must be ThemeSizing and ThemeWidthSpecific. In this case, because there is no overlap between the two, this is never possible so Typescript shows the never error.

    The easy fix: type ThemeWidth = ThemeSizing | ThemeWidthSpecific

    A side note: if the two types ThemeSizing and ThemeWidthSpecific had a common string, then ThemeWidth could only equal that common string when using the intersection.