我有一个React钩子,它将把对象的内容写入URL搜索参数,并为每个键接受一个“decoder”参数,以便将参数从字符串解析/转换回其原始类型。该钩子接受一个描述对象形状的类型参数,并且总体上使用一个洞进行完整类型化:我无法让它从类型参数推断解码器返回值。
钩子是否可以对输入对象和解码器返回值使用一个类型参数?
目前:
1.如果T中的密钥没有解码器,则TS会抱怨
1.如果解码器在T中没有对应的密钥,则TS抱怨
1.如果setQueryParams参数与T不匹配,则TS将发出投诉
然而,解码器返回值被忽略,并且所有queryParam值基于T被类型化。
export function useTypedQueryParams<T extends Record<string, unknown>>(
paramDecoders: Record<keyof T, (value?: string) => T[keyof T]>,
): [T, (params: T) => void] {
const { search } = useLocation();
const history = useHistory();
const paramDecodersRef = useRef(paramDecoders);
const baseQueryParams = useMemo(
() =>
Object.entries(queryString.parse(search)).reduce(
(acc, [key, value]) =>
!paramDecodersRef.current[key as keyof T]
? {
...acc,
[key]: value,
}
: acc,
{},
),
[search],
);
const parsed: T = useMemo(() => {
const parsedQuery: Record<string, string> = queryString.parse(search);
return Object.keys(paramDecodersRef.current).reduce(
(parsedParams, paramKey) => {
const decoder = paramDecodersRef.current[paramKey];
const value = parsedQuery[paramKey as string];
return {
...parsedParams,
[paramKey]: decoder ? decoder(value) : value,
};
},
{} as T,
);
}, [search]);
const setQueryParams = useCallback(
(updatedParams: T) => {
const newQueryString = queryString.stringify({
...baseQueryParams,
...updatedParams,
});
history.push({
search: `?${newQueryString}`,
});
},
[history, baseQueryParams],
);
return [parsed, setQueryParams];
}
用法:
const [queryParams, setQueryParams] = useTypedQueryParams<{
foo: string | null;
bar: number | null;
}>({
foo: (value?: string) => value || null,
// TODO: TS won't catch that bar can be a string instead of a number
bar: (value?: string) => value || null,
});
1条答案
按热度按时间8qgya5xd1#
答案来自@jonrsharpe的建议/评论:
T[keyofT]是所有键的值类型的并集,在本例中为string|数|如果您需要特定键的值类型,请尝试Map类型-tsplay.dev/WJAG5m。
执行情况