typescript 键入已解析URLSearchParams的输入/输出

5sxhfpxr  于 2023-03-13  发布在  TypeScript
关注(0)|答案(1)|浏览(194)

我有一个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,
});
8qgya5xd

8qgya5xd1#

答案来自@jonrsharpe的建议/评论:
T[keyofT]是所有键的值类型的并集,在本例中为string|数|如果您需要特定键的值类型,请尝试Map类型-tsplay.dev/WJAG5m。

执行情况

type ParamDecoders<T> = {
  [K in keyof T]: (value?: string) => T[K];
};
export function useTypedQueryParams<T extends Record<string, unknown>>(
  paramDecoders: ParamDecoders<T>,
): [T, (params: T) => void] {...}

相关问题