skip to Main Content

I have this react hook which returns filter based on type props user gave.

interface Props {
    type: 'product' | 'content';
}

function useFilterSelector(props: Props) {
    const { t } = useClientTranslation();

    const queryClient = useQueryClient();
    const [filterSelector] = useState(() => {
        switch (props.type) {
            case 'product':
                return new ProductFilter(t, queryClient);
            case 'content':
                return new ContentFilter(t, queryClient);
        }
    });

    return { filterSelector };
}

This hook works fine but the problem is that I can’t get strict type inference
For example If developer provides "product" for an arguments

  const { filterSelector } = useFilterSelector({ type : "product"})

The infered type should be

type = ProductFilter

but i get

type = ProductFilter | ContentFilter

I tried this

function useFilterSelector(props: Props) {
    const { t } = useClientTranslation();

    type FilterType = (typeof props)['type'];
    type FilterSelector = FilterType extends 'product'
        ? ProductFilter
        : FilterType extends 'content'
        ? ContentFilter
        : unknown;

    const queryClient = useQueryClient();
    const [filterSelector] = useState<FilterSelector>(() => {
        switch (props.type) {
            case 'product':
                return new ProductFilter(t, queryClient);
            case 'content':
                return new ContentFilter(t, queryClient);
        }
    });

    return { filterSelector };
}

but the type is inferred as unknown

how do i fix this?

2

Answers


  1. How about type assertion so that typescript will use the type instead of unknown?

    switch (props.type) {
          case 'product':
            return new ProductFilter(t, queryClient) as FilterSelector;
          case 'content':
            return new ContentFilter(t, queryClient) as FilterSelector;
        }
    
    Login or Signup to reply.
  2. You can make use of conditional return type based on type of props.type

    interface Props {
        type: 'product' | 'content';
    }
    
    interface ProductFilter {
      a: string;
    }
    interface ContentFilter {
      b: number;
    }
    
    interface FilterSelector<T extends Props> {
      filterSelector: T['type'] extends 'product' ? ProductFilter : ContentFilter
    }
    
    function useFilterSelector<T extends Props>(props: T): FilterSelector<T> {
      return { filterSelector: {} as any }
    }
    
    const pF = useFilterSelector({ type: 'product' })
    pF.filterSelector.a
    //               ^? (property) ProductFilter.a: string
    const cF = useFilterSelector({ type: 'content' })
    cF.filterSelector.b
    //               ^? (property) ContentFilter.b: number
    

    Playground link

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search