reactjs - Como inferir o tipo com base no valor em javascript
Eu tenho este hook do React que retorna um filtro baseado nas props de tipo que o usuário forneceu.
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 };
}
Este hook funciona bem, mas o problema é que não consigo obter inferência de tipo estrita. Por exemplo, se o desenvolvedor fornecer "produto" como argumento
const { filterSelector } = useFilterSelector({ type : "product"})
O tipo inferido deve ser
type = ProductFilter
mas eu consigo
type = ProductFilter | ContentFilter
Eu tentei isso
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 };
}
mas o tipo é inferido como desconhecido
como eu conserto isso?
Resposta #1
Você pode fazer uso de tipo de retorno condicional baseado no tipo de 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
Link do playground
Resposta #2
Que tal a asserção de tipo para que o TypeScript use o tipo em vez de unknown?
switch (props.type) {
case 'product':
return new ProductFilter(t, queryClient) as FilterSelector;
case 'content':
return new ContentFilter(t, queryClient) as FilterSelector;
}
