网站搜索

reactjs - 如何根据 JavaScript 值推断类型


我有这个 React 钩子,它根据用户提供的类型属性返回过滤器。

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 };
}

这个钩子可以正常工作,但问题是我无法获得严格的类型推断。例如,如果开发者为参数提供“product”

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

推断的类型应该是

type = ProductFilter

但是我得到

type = ProductFilter | ContentFilter

我尝试过这个

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 };
}

但是该类型被推断为未知

我该怎么修复这个?

答案#1

你可以根据 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

游乐场链接

答案#2

类型断言如何,这样 TypeScript 会使用该类型而不是 unknown?

switch (props.type) {
      case 'product':
        return new ProductFilter(t, queryClient) as FilterSelector;
      case 'content':
        return new ContentFilter(t, queryClient) as FilterSelector;
    }