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

TypeScript 如何正确为带可选参数的函数定义类型

问题描述

现有如下TypeScript函数定义:

function test<S, T>(obj: S, prop: keyof S, mapper?: (value: S[keyof S]) => T): S[keyof S] | T {
  return typeof mapper === 'function'
    ? mapper(obj[prop])
    : obj[prop];
}

不传入mapper参数调用时,返回值类型无法被正确推导:

const value = test({ a: 'stringValue' }, 'a'); // value 类型被推导为 unknown

但传入恒等函数作为第三个参数时,返回值类型可以被正确推导:

const value = test({ a: 'stringValue' }, 'a', x => x); // value 类型被正确推导为 string

需要调整test函数的类型定义,实现未传入mapper参数时返回值类型也能被正确推导。

问题原因

原有类型定义存在两个核心问题:

  • 没有为传入的属性键prop声明独立泛型约束,使用S[keyof S]只能拿到对象所有属性值的联合类型,无法匹配传入单个属性对应的精确值类型,即使传入mapper也无法精准推导对应属性的类型
  • 泛型参数T仅在可选的mapper参数中被引用,当未传入mapper时,TypeScript无法推断T的实际类型,会默认将其填充为unknown,导致最终返回值类型被折叠为unknown
解决方案

使用TypeScript函数重载分别定义两种调用场景的类型,同时为属性键增加独立泛型参数,用来推导精确的属性值类型:

// 重载1:未传入mapper,直接返回对应属性的精确类型
function test<S, K extends keyof S>(obj: S, prop: K): S[K];
// 重载2:传入mapper,返回mapper处理后的结果类型
function test<S, K extends keyof S, T>(obj: S, prop: K, mapper: (value: S[K]) => T): T;
// 函数实现,内部类型兼容所有重载场景即可
function test<S, K extends keyof S, T>(obj: S, prop: K, mapper?: (value: S[K]) => T): S[K] | T {
  return typeof mapper === 'function'
    ? mapper(obj[prop])
    : obj[prop];
}
效果验证

调整类型后,所有调用场景的返回值都能被正确推导:

const value1 = test({ a: 'stringValue', b: 123 }, 'a'); // 推导为string类型
const value2 = test({ a: 'stringValue', b: 123 }, 'b'); // 推导为number类型
const value3 = test({ a: 'stringValue', b: 123 }, 'a', x => x.length); // 推导为number类型
const value4 = test({ a: 'stringValue', b: 123 }, 'b', x => x.toString()); // 推导为string类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:15:28