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

为何该TypeScript条件类型静态有效但动态调用失效?

问题成因

动态调用时类型校验失败的核心原因是TypeScript对testObject的默认类型推断行为:

  • 直接声明const testObject = { arr: ['a', 'b', 'c'] }时,TypeScript会把arr的类型推断为宽数组string[],而非精确的元组['a', 'b', 'c']。
  • 你的ArrayIndices类型依赖数组的length属性为具体字面量数字(元组的length是固定字面量)才能递归生成索引联合类型;但普通数组的length是number类型,无法触发递归终止条件,最终ArrayIndices<string[]>会返回never。
  • 因此Path<{ arr: string[] }>的数组分支中,${Key & string}[${ArrayIndices<T[Key]>}]会变成never,仅剩下"arr"这一个有效路径,导致传入'arr[0]'时类型不匹配。
可行解决办法

办法1:使用as const断言强制推断元组类型

通过as const让TypeScript把数组推断为只读元组,保留精确的长度和元素类型:

const testObject = { arr: ['a', 'b', 'c'] } as const;

const testFunction = <T, P extends Path<T>>(obj: T, path: P) => {};

testFunction(testObject, 'arr[0]'); // 类型校验通过

注意:as const会把数组设为只读类型,若后续需要修改数组元素,可结合Readonly<T>或手动调整类型。

办法2:手动指定testObject的元组类型

显式声明arr为元组类型,避免TypeScript自动推断为宽数组:

const testObject: { arr: ['a', 'b', 'c'] } = { arr: ['a', 'b', 'c'] };

testFunction(testObject, 'arr[0]'); // 类型校验通过

这种方式既保留了元组的精确类型,也不限制数组的可写性,灵活性更高。

办法3:修改ArrayIndices兼容普通数组(牺牲精确性换通用性)

如果需要支持长度不固定的动态数组,可修改ArrayIndices,当数组长度为number时返回number类型,让模板字符串匹配任意数字索引:

// 拆分递归逻辑,兼容元组和普通数组
type _ArrayIndices<
  N extends any[],
  Acc extends number[] = []
> = Acc['length'] extends N['length']
  ? Acc[number]
  : _ArrayIndices<N, [...Acc, Acc['length']]>;

type ArrayIndices<N extends any[]> = N extends { length: infer L }
  ? L extends number
    ? number extends L // 判断是否为普通数组(length为number而非字面量)
      ? number
      : _ArrayIndices<N>
    : never
  : never;

// 原Path类型无需修改
type Path<T, Key = keyof T> = Key extends keyof T
  ? T[Key] extends unknown[]
    ? `${Key & string}[${ArrayIndices<T[Key]>}]` | `${Key & string}`
    : T[Key] extends object
    ? T[Key] extends infer R
      ? `${Key & string}.${Path<R, keyof R>}` | `${Key & string}`
      : never
    : `${Key & string}`
  : never;

const testObject = { arr: ['a', 'b', 'c'] }; // 依旧是string[]类型
testFunction(testObject, 'arr[0]'); // 类型校验通过
testFunction(testObject, 'arr[100]'); // 也会通过,因为索引被推断为number类型

这种方式的 trade-off 是失去了精确的索引范围校验,但能兼容动态数组场景。

内容的提问来源于stack exchange,提问作者Joseph Beuys' Mum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:35:00