Problem Description:

我有一个自定义的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,这可能会导致此挂钩的使用者出现歧义.

Goal:

我想在挂钩内实现一个断言机制,以确保options严格遵守基于returnForm参数确定的类型.这样,使用挂钩的消费者就可以自信地依赖推断出的类型,而无需进行额外的类型判断.

Code Sample:

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

Additional Notes:

我在useFetchSelectOptions挂钩中添加了一个基本类型断言,以根据returnForm参数细化options的类型.这确保了options将符合预期的SelectOption[] | nullRecord<string, string> | null,具体取决于指定的returnForm.

我正在寻求对这种方法的反馈以及任何潜在的改进或替代策略,以在挂钩的上下文中实现严格输入options.

感谢您的帮助!

推荐答案

使用挂钩的消费者可以自信地依赖推断的类型,而无需进行额外的类型判断.

因为挂钩的返回类型已经被输入为[T[] | null, SelectOption[] | Record<string, string> | null],所以这就是调用者将看到的全部内容.IIUC,哪个不是您要寻找的?

挂钩内部的断言机制

无论做什么inside,函数做什么not都会影响"外部",因为返回类型已经定义,如上所述.TS只会判断返回的实际值是否满足所述返回类型.


如果您希望调用者看到与returnForm值对应的返回类型,那么您应该处理Hook函数签名.就您的情况而言,function overload可能是最合适的:

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]

Playground Link

Typescript相关问答推荐

为什么在TypScript中将类型守护的返回类型写成This is(

替换类型脚本类型中的多个特定字符串

如何访问Content UI的DatePicker/中的sx props of year picker?'<>

类型脚本类型字段组合

了解类型规则中的关键字

角效用函数的类型推断

React Typescript项目问题有Redux-Toolkit userSlice角色问题

记录键的泛型类型

迭代通过具有泛型值的映射类型记录

如何合并两个泛型对象并获得完整的类型安全结果?

如何以Angular 导入其他组件S配置文件

刷新时保持当前名称的Angular

TYPE FOR ARRAY,其中每个泛型元素是单独的键类型对,然后在该数组上循环

Typescript -返回接口中函数的类型

将超类型断言为类型脚本中的泛型参数

如何在具有嵌套数组的接口中允许新属性

从以下内容之一提取属性类型

使用泛型以自引用方式静态键入记录的键

Angular另一个组件的nativeelement未定义

根据索引将元组拆分为两个块