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

如何让TypeScript正确推断嵌套泛型中的字符串字面量类型?

让TypeScript正确推断嵌套泛型中的字符串字面量类型

问题描述

我定义了一个可任意嵌套的泛型类型,目前TypeScript会将其中的相关类型推断为string,但我希望它能推断为字符串字面量类型。相关代码如下:

// 从字符串类型派生新类型
type DeriveType<T extends string, Base extends string> = `${Base}${T}`;

// 可任意嵌套的类型
type Nested<T extends string, Base extends string = ''> = {
  value: T;
  derived(path: DeriveType<T, Base>): void;
  children?: Nested<string, `${Base}${T}`>[]; // 这里需要修改
};

declare function makeNested<T extends string>(nested: Nested<T>): Nested<T>;

makeNested({
  value: '/parent', // value类型为 "/parent"
  derived(path) {}, // path正确推断为 "/parent"
  children: [
    {
      value: '/child', // value类型被推断为string,而非"/child"
      derived(path) {}, // path被错误推断为 `/parent${string}`
      // 期望推断为 "/parent/child"
    },
  ],
});

解决方案

问题核心在于Nested<string, ${Base}${T}>的定义:直接使用string会让TypeScript放弃字面量类型推断,转而使用宽泛的string类型。我们需要调整类型定义,让TypeScript能递归捕获每个层级的字面量类型。

修改后的代码

// 保留路径拼接逻辑
type DeriveType<T extends string, Base extends string> = `${Base}${T}`;

// 重新定义递归嵌套类型,自动推断子节点字面量
type Nested<Base extends string = ''> = {
  value: string;
  derived(path: DeriveType<this['value'], Base>): void;
  children?: Nested<`${Base}${this['value']}`>[];
};

// 调整函数声明,让TypeScript自动推断整个嵌套结构类型
declare function makeNested<T extends Nested>(nested: T): T;

// 现在所有层级的类型都会被正确推断
makeNested({
  value: '/parent', // 类型为 "/parent"
  derived(path) {}, // path 推断为 "/parent"
  children: [
    {
      value: '/child', // 类型为 "/child"
      derived(path) {}, // path 推断为 "/parent/child"
      children: [
        {
          value: '/grandchild', // 类型为 "/grandchild"
          derived(path) {} // path 推断为 "/parent/child/grandchild"
        }
      ]
    }
  ],
});

原理说明

  1. 将Nested的泛型参数调整为仅跟踪当前基础路径Base,不再绑定顶层的T,让每个节点的类型推断独立且递归。
  2. 使用this['value']引用当前节点的value字面量类型,配合DeriveType生成精准的路径类型。
  3. 子节点的Nested泛型参数自动拼接当前路径与当前节点的value,形成新的基础路径,实现层级间的类型传递。
  4. makeNested函数直接接受Nested类型参数,让TypeScript自动推断整个嵌套结构的字面量类型,而非仅推断顶层类型。

更简洁的写法

如果不需要单独定义DeriveType,可以直接在类型中拼接路径:

type Nested<Base extends string = ''> = {
  value: string;
  derived(path: `${Base}${this['value']}`): void;
  children?: Nested<`${Base}${this['value']}`>[];
};

declare function makeNested<T extends Nested>(nested: T): T;

内容的提问来源于stack exchange,提问作者Allan J.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:13:16