You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript键重映射时如何访问泛型类型以实现类型安全

TypeScript 对象键重映射与泛型类型安全实现

问题描述

需要实现工具函数,将传入的资源对象的键从key重映射为useKey格式,同时将每个资源转换为带泛型参数的函数,确保最终生成的Hook具备完整类型安全。

示例用法:

const { useMyAwesomeResource } = handleResources({
  resources: {
    // TProps: string, TItem: AwesomeResource
    myAwesomeResource: handleResource<string, AwesomeResource>({
      // 自定义配置
    })
  }
})

期望最终类型:

useMyAwesomeResource: (resourceId: string) => HookResult<AwesomeResource>

当前实现中,HookFuncs类型无法正确访问每个资源携带的泛型TProps和TItem,导致类型丢失,核心问题代码如下:

// 结果类型定义
type HookResult<TItem> = {
  data: TItem
}

type HookFunc<TItem, TProps> = (props: TProps) => HookResult<TItem>

type HookFuncs<TResourceHandlers> = {
  [Key in keyof TResourceHandlers as `use${Capitalize<
    string & Key
  >}`]: HookFunc<TResourceHandlers[Key]> // 此处无法正确提取泛型参数
}

// 配置类型
type ResourceHandler<TRawItem, TItem> = {}

interface Config {
  resources: {
    [resourceName: string]: ResourceHandler<any, any>
  }
}

export const handleResources = (
  config: Config
): HookFuncs<typeof config.resources> => {
   // 逻辑实现
})

// 资源创建函数
const handleResource = <TItem, TProps = {}>(
  resourceConfig: ResourceConfig<TItem, TProps>
): ResourceInfo<TItem, TProps> => resourceConfig

解决方案

核心是让每个资源处理器携带明确的泛型类型信息,同时将handleResources改为泛型函数,保留传入资源的完整类型,再通过条件类型提取每个资源的TProps和TItem:

1. 修正资源相关类型定义

确保ResourceInfo(handleResource的返回类型)明确携带TProps和TItem:

// 自定义资源配置类型(可根据实际需求补充字段)
type ResourceConfig<TItem, TProps> = {
  // 比如请求地址、转换逻辑等配置项
}

// 资源处理器类型,用于携带泛型参数信息
type ResourceInfo<TProps, TItem> = {
  _propsType?: TProps
  _itemType?: TItem
}

// Hook返回结果类型
type HookResult<TItem> = {
  data: TItem
}

// Hook函数类型
type HookFunc<TProps, TItem> = (props: TProps) => HookResult<TItem>

2. 实现带泛型提取的HookFuncs类型

通过条件类型从ResourceInfo中提取TProps和TItem:

// 提取ResourceInfo中的TProps类型
type ExtractProps<T> = T extends ResourceInfo<infer P, any> ? P : never

// 提取ResourceInfo中的TItem类型
type ExtractItem<T> = T extends ResourceInfo<any, infer I> ? I : never

// 重映射键并生成对应的Hook类型
type HookFuncs<TResources extends Record<string, ResourceInfo<any, any>>> = {
  [Key in keyof TResources as `use${Capitalize<string & Key>}`]: HookFunc<
    ExtractProps<TResources[Key]>,
    ExtractItem<TResources[Key]>
  >
}

3. 修正handleResources为泛型函数

不再使用固定的Config接口,用泛型保留传入资源的完整类型:

export const handleResources = <TResources extends Record<string, ResourceInfo<any, any>>>(
  config: { resources: TResources }
): HookFuncs<TResources> => {
  const result = {} as HookFuncs<TResources>
  
  Object.entries(config.resources).forEach(([key, resource]) => {
    const hookKey = `use${key.charAt(0).toUpperCase() + key.slice(1)}` as keyof HookFuncs<TResources>
    result[hookKey] = (props) => {
      // 替换为实际资源处理逻辑,比如请求数据并返回HookResult
      return { data: {} as ExtractItem<typeof resource> }
    }
  })
  
  return result
}

4. 修正handleResource函数

确保返回类型正确关联泛型参数:

const handleResource = <TProps, TItem>(
  resourceConfig: ResourceConfig<TItem, TProps>
): ResourceInfo<TProps, TItem> => {
  return { ...resourceConfig } as ResourceInfo<TProps, TItem>
}

测试示例

type AwesomeResource = { id: string; name: string }

const { useMyAwesomeResource } = handleResources({
  resources: {
    myAwesomeResource: handleResource<string, AwesomeResource>({})
  }
})

// useMyAwesomeResource类型自动推导为:(props: string) => HookResult<AwesomeResource>
const result = useMyAwesomeResource('123')
// result.data 类型为 AwesomeResource,具备完整类型校验

关键说明

  • 通过ResourceInfo携带泛型类型信息,让TypeScript可以通过infer关键字提取对应的TProps和TItem
  • handleResources使用泛型参数TResources保留传入资源的完整类型,避免用any丢失类型信息
  • 键重映射通过as关键字结合Capitalize工具类型实现,确保生成的Hook名称符合约定

内容的提问来源于stack exchange,提问作者jared_hexagon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:43:25