问题描述:
我有一个自定义的 React hook
useFetchSelectOptions
,它可以获取数据并将其格式化为可选选项。根据 returnForm
参数,选项需要为 SelectOption[] | null
或 `Record我想确保钩子内
options
的返回类型与指定的returnForm
严格匹配。例如:
returnForm
为 'arr'
,选项应为 SelectOption[] | null
类型。returnForm
为 'dict'
,选项应为 Record<string, string> | null
类型。目前,由于钩子中的条件格式,
options
类型可能是Record<string, string> | SelectOption[] | null
,这可能会导致该钩子的使用者产生歧义。
目标:
我想在钩子内部实现断言机制,以确保
options
严格遵守基于returnForm
参数确定的类型。这样,使用钩子的消费者就可以放心地依赖推断的类型,而无需额外的类型检查。
代码示例:
import { useState, useEffect } from 'react';
export interface SelectOption {
label: string;
value: string;
}
// Define a type for the return form
type ReturnForm = 'dict' | 'arr';
export function useFetchSelectOptions<T extends { name: string; id: string }>(
fetchData: () => Promise<T[]>,
returnForm: ReturnForm = 'arr', // Default to array format if not specified
): [T[] | null, SelectOption[] | Record<string, string> | null] {
const [data, setData] = useState<T[] | null>(null);
useEffect(() => {
const fetchDataAndMapToOptions = async () => {
try {
const fetchedData = await fetchData();
setData(fetchedData);
} catch (error) {
console.error('Error fetching data:', error);
setData(null);
}
};
fetchDataAndMapToOptions();
}, [fetchData]);
let options: SelectOption[] | Record<string, string> | null = data
? data.map((item) => ({ label: item.name, value: item.id }))
: null;
// Assertion to strictly type 'options' based on 'returnForm'
if (returnForm === 'dict') {
options = options?.reduce(
(acc, curr) => {
if (curr) {
acc[curr.value] = curr.label;
}
return acc;
},
{} as Record<string, string>,
) ?? null;
}
return [data, options];
}
补充说明:
我在
useFetchSelectOptions
挂钩中添加了基本类型断言,以基于 options
参数细化 returnForm
的类型。这可确保 options
将符合预期的 SelectOption[] | null
或 Record<string, string> | null
,具体取决于指定的 returnForm
。
我正在寻求有关此方法的反馈以及任何潜在的改进或替代策略,以在钩子的上下文中实现严格的
options
键入。
感谢您的协助!
使用钩子的消费者可以放心地依赖推断的类型,而无需额外的类型检查。
因为钩子的返回类型已经输入为
[T[] | null, SelectOption[] | Record<string, string> | null]
,所以调用者将看到这一切。这不是您要找的东西,IIUC?
钩子内部的断言机制
无论在内部做什么,函数都不会影响“外部”,因为返回类型已经定义,如上所述。 TS只会检查返回的实际值是否满足所述返回类型。
如果您希望调用者看到与
returnForm
值对应的返回类型,那么您应该研究 Hook 函数签名。对于您的情况,函数重载可能是最合适的:
export function useFetchSelectOptions<T extends { name: string; id: string }>(fetchData: () => Promise<T[]>, returnForm?: "arr"): [T[], SelectOption[] | null]
export function useFetchSelectOptions<T extends { name: string; id: string }>(fetchData: () => Promise<T[]>, returnForm: "dict"): [T[], Record<string, string> | null]
export function useFetchSelectOptions<T extends { name: string; id: string }>(
fetchData: () => Promise<T[]>,
returnForm: ReturnForm = 'arr', // Default to array format if not specified
): [T[] | null, SelectOption[] | Record<string, string> | null] {
// Implementation (untouched)
}
const arr = useFetchSelectOptions(async () => []);
// ^? [never[], SelectOption[] | null]
const dict = useFetchSelectOptions(async () => [], "dict");
// ^? [never[], Record<string, string> | null]