Search code examples
typescripttypescript-typingsreact-typescript

Typescript: using array values as union for related prop


I have the following React component

const Pagination = <P extends number[] = [10, 25, 50, 100]>(props: {
    perPageVariants?: P,
    perPageDefault?: P[number],
}) => 
{
    // ....
}

It works perfectly with default value for perPageVariants prop and doesn't allow to use wrong value for perPageDefault. But if I set custom perPageVariants values, perPageDefault continues to allow only value from defaults.

<Pagination 
    perPageVariants={[123, 456]} 
    perPageDefault={100}
/>
// it works, but should throw an error 

Is it possible to do this somehow with Typescript?

Additional question:

Is it possible somehow to move defaultPerPageVariants to the external variable to have ability to configure it in one place? I tried this, but it fails -

type Props<P extends number[]> = {
  perPageVariants?: [...P],
  perPageDefault?: [...P][number],
}

const defaultPerPageVariants = [10, 25, 50, 100, 200] as const;

const Pagination = <P extends number[] = typeof defaultPerPageVariants>(props: Props<P>) => {
  const {
    perPageVariants = defaultPerPageVariants,
    perPageDefault = defaultPerPageVariants[0],
  } = props;

  return null;
};

It fails with the following error -

Type 'readonly [10, 25, 50, 100, 200]' does not satisfy the constraint 'number[]'. The type 'readonly [10, 25, 50, 100, 200]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.

But I can't define defaultPerPageVariants without as const because it will be number[] type and break restriction for perPageDefault prop in case if perPageVariants is not specified.


Solution

  • You just need to infer each element in the tuple:

    import React from 'react'
    
    const Pagination = <P extends number[] = [10, 25, 50, 100]>(props: {
      perPageVariants?: [...P],
      perPageDefault?: [...P][number],
    }) => {
      return null
    }
    
    <Pagination perPageDefault={1} perPageVariants={[123, 456]} />;// expected error
    <Pagination perPageDefault={123} perPageVariants={[123, 456]} />; // ok
    

    enter link description here