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

TypeScript中如何检测函数参数的具体类型?

TypeScript泛型类型检测实现方案

问题背景

原代码

type ValueType = string | number

interface I<X extends ValueType>{
  f: (value: X) => void
}

const run = <X extends ValueType>(i: I<X>) => {
  i.f(2)
}

报错信息

TS2345: Argument of type 'number' is not assignable to parameter of type 'X'.   
'number' is assignable to the constraint of type 'X', but 'X' could be instantiated with a different subtype of constraint 'ValueType'.

需求说明

用户希望避免直接用as断言解决报错,而是先判断泛型X的实际类型(string或number)再针对性调用函数,伪代码如下:

const run = <X extends ValueType>(i: I<X>) => {
  if (typeof X === 'string'){
    const _f = i.f as (value: string) => void
    _f("jack")
  }else if (typeof X === 'number'){
    const _f = i.f as (value: number) => void
    _f(1)
  }
}

实现方案

TypeScript泛型是编译时类型信息,运行时不存在X这个类型标识,无法直接通过typeof X检测。需通过运行时可访问的信息关联编译时类型,以下是几种可靠实现方式:

方案1:给接口添加类型标记(最推荐)

修改接口,新增用于区分类型的属性,让TS自动完成类型收窄,无需额外断言:

type ValueType = string | number

// 基础接口
interface IBase<X extends ValueType> {
  f: (value: X) => void
}

// 带类型标记的子接口
interface IString extends IBase<string> {
  type: 'string'
}

interface INumber extends IBase<number> {
  type: 'number'
}

// 统一类型别名
type I<X extends ValueType> = X extends string ? IString : INumber

const run = (i: I<string> | I<number>) => {
  if (i.type === 'string') {
    i.f("jack") // TS自动推断参数为string类型
  } else {
    i.f(1) // TS自动推断参数为number类型
  }
}

方案2:使用类型谓词函数

通过自定义类型谓词,结合运行时判断逻辑(需根据业务场景补充)实现类型收窄:

type ValueType = string | number

interface I<X extends ValueType>{
  f: (value: X) => void
}

// 类型谓词:判断传入的I实例是否为I<string>
function isStringI(i: I<ValueType>): i is I<string> {
  // 实际场景可根据业务逻辑添加运行时判断条件,比如关联属性、测试调用等
  return true
}

const run = <X extends ValueType>(i: I<X>) => {
  const typedI = i as I<ValueType>
  if (isStringI(typedI)) {
    typedI.f("jack")
  } else {
    typedI.f(1)
  }
}

方案3:重载函数

为run定义重载签名,明确不同类型参数对应的处理逻辑:

type ValueType = string | number

interface I<X extends ValueType>{
  f: (value: X) => void
}

// 重载签名
function run(i: I<string>): void
function run(i: I<number>): void

// 实现函数
function run(i: I<ValueType>) {
  // 运行时可结合实际场景添加判断逻辑,比如通过函数参数的特征
  if (/* 自定义运行时判断条件 */) {
    i.f("jack" as never)
  } else {
    i.f(1 as never)
  }
}

关键提示

泛型仅存在于编译阶段,运行时无法直接获取泛型类型。所有类型分支逻辑都需要依赖运行时可访问的标识(如类型标记属性)来关联编译时类型,其中方案1的类型标记方式最安全、最符合TS类型系统设计。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:07:27