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

如何解决Enum类型作为键时的类型识别错误?需保留类型提示

解决TypeScript枚举作为对象键时的路径类型识别问题

问题核心

当对象使用字符串枚举作为计算属性键时,现有的Path和PathValue类型无法正确识别形如'bar.width'的字符串路径,导致类型推导失败(如示例中的t2测试不通过),但普通字符串键的路径(如'bar.foo.baz')可以正常工作。

问题原因

  1. 原代码中PathImpl的Key extends string条件会排除枚举成员类型(字符串枚举的成员类型并非string,而是独特的枚举成员类型),导致生成的路径集合中不包含枚举键对应的字符串路径。
  2. PathValue在解析字符串路径时,无法将字符串形式的键(如'width')映射到对应的枚举成员键(如style.WIDTH),从而无法正确获取属性类型。

解决方案

通过模板字符串类型的特性,将枚举成员类型转换为对应的字符串值类型,同时修改路径生成和值解析的逻辑,确保字符串路径能正确匹配枚举键。

修改后的完整代码

type IsAny<T> = unknown extends T
    ? [keyof T] extends [never]
        ? false
        : true
    : false;

/* eslint-disable @typescript-eslint/no-explicit-any */
export type PathImpl<T, Key extends keyof T> = `${Key}` extends string
    ? IsAny<T[Key]> extends true
        ? never
        : T[Key] extends Record<string, any>
          ?
                | `${Key}.${PathImpl<T[Key], Exclude<keyof T[Key], keyof any[]>>}`
                | `${Key}.${Exclude<keyof T[Key], keyof any[]>}`
          : never
    : never;
/* eslint-enable @typescript-eslint/no-explicit-any */

export type PathImpl2<T> = PathImpl<T, keyof T> | `${keyof T}`;

export type Path<T> = `${keyof T}` extends string
    ? PathImpl2<T> extends infer P
        ? P extends string
            ? P
            : `${keyof T}`
        : `${keyof T}`
    : never;

type GetValueByStringPath<T, P extends string> = 
    P extends `${infer Key}.${infer Rest}`
        ? (
            K extends keyof T 
                ? `${K}` extends Key 
                    ? GetValueByStringPath<T[K], Rest> 
                    : never 
                : never
        ) extends infer V
            ? V extends never ? never : V
            : never
        : (
            K extends keyof T 
                ? `${K}` extends P 
                    ? T[K] 
                    : never 
                : never
        ) extends infer V
          ? V extends never ? never : V
          : never;

export type PathValue<T, P extends Path<T>> = GetValueByStringPath<T, P extends string ? P : `${P}`>;

enum style {
    HEIGHT = 'height',
    WIDTH = 'width'
}

type Props = {
    foo: string;
    bar: {
        street: string;
        [style.HEIGHT]: string;
        [style.WIDTH]: string;
        foo: {
            baz: number;
        }
    };
};

declare function useProp<P extends Path<Props>>(path: P): PathValue<Props, P>;

import type { Equal, Expect } from '@type-challenges/utils'

// 测试通过
type t1 = Expect<Equal<PathValue<Props, 'bar.foo.baz'>, number>>

// 现在测试通过
type t2 = Expect<Equal<PathValue<Props, 'bar.width'>, string>>

关键修改点说明

  1. 路径生成逻辑:

    • 将PathImpl中的Key extends string改为${Key} extends string,利用模板字符串类型自动将枚举成员转换为对应字符串值的特性,确保枚举键能被纳入路径生成。
    • PathImpl2和Path中统一使用${keyof T}将所有键转换为字符串类型,保证路径集合的一致性。
  2. 值解析逻辑:

    • 新增GetValueByStringPath类型,通过匹配字符串路径与对象键的字符串表示(${K} extends P),将字符串路径映射到对应的对象键(包括枚举键),从而正确推导属性类型。
    • PathValue基于GetValueByStringPath实现,确保字符串路径能正确解析到对应的属性值类型。

验证效果

  • 原测试t1保持通过,t2现在也能正常通过。
  • 保留完整的路径提示功能:在调用useProp时,输入路径会自动提示所有合法的字符串路径,包括枚举键对应的'bar.height'、'bar.width'等。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:39:52