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

TypeScript直接使用类型与带extends的泛型为何表现不同?

泛型与非泛型函数的类型表现差异解析

问题描述

我搞不懂为什么用泛型和直接用具体类型写的函数,在类型推断上表现完全不一样。这个问题源自Stack Overflow上的回答《How to create a relation between parameters of a function?》。

代码示例:

interface Data {
  a: number,
  b: { x: number, y: number}
}

type GetKeyValueTuples<T> = { [Key in keyof T]: [Key, T[Key]] }[keyof T];

function getChangedDataByProperty<T extends Data>(
  ...[data, changedProp, newPropValue]: [T, ...GetKeyValueTuples<T>]
) {
  if (changedProp === "b") {
    return {
      ...data,
      b: {
        // 疑问:这里newPropValue的类型为什么是number、"a"、"b"的联合?不是应该是{b的类型}吗?
        x: newPropValue.x,
        y: newPropValue.y,
      }
    }
  } else {
    return {
      ...data,
      x: newPropValue
    }
  }
}

// 疑问:为什么这个不用泛型的函数,类型推断就正常?
function getChangedDataByProperty2(
  ...[data, changedProp, newPropValue]: [Data, ...GetKeyValueTuples<Data>]
) {
  if (changedProp === "b") {
    return {
      ...data,
      b: {
        x: newPropValue.x,
        y: newPropValue.y,
      }
    }
  } else {
    return {
      ...data,
      x: newPropValue
    }
  }
}

差异原因拆解

非泛型函数的精准推断

getChangedDataByProperty2直接用Data作为类型参数,此时GetKeyValueTuples<Data>会被直接解析成具体的联合类型:

["a", number] | ["b", { x: number, y: number }]

当你用changedProp === "b"做条件判断时,TypeScript能准确做类型窄化——它知道一旦changedProp是"b",newPropValue必然对应{ x: number, y: number },所以访问x、y完全没问题。

泛型函数的类型局限

getChangedDataByProperty用了T extends Data,这里的T可以是Data的任意子类型(比如你可以传入一个把b改成其他类型的对象)。TypeScript无法提前确定T的具体结构,所以GetKeyValueTuples<T>会被推断成一个宽泛的联合:

[keyof T, T[keyof T]]

当你判断changedProp === "b"时,TypeScript没法确定T里的"b"对应的类型是不是和原始Data一致(毕竟T可以重定义b),所以newPropValue的类型依然是T[keyof T](也就是T["a"] | T["b"]的联合),自然没法直接访问x、y。

修复方案

要让泛型函数实现参数间的类型绑定,得把键和对应值的关联明确写出来,比如改成这种写法:

function getChangedDataByProperty<K extends keyof Data>(
  data: Data,
  changedProp: K,
  newPropValue: Data[K]
) {
  if (changedProp === "b") {
    return {
      ...data,
      b: { ...newPropValue }
    }
  } else {
    return {
      ...data,
      [changedProp]: newPropValue
    }
  }
}

这里直接把泛型参数K绑定到Data的键上,newPropValue的类型就是Data[K],TypeScript能准确识别窄化后的类型,不会再出现错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 10:01:43