You need to enable JavaScript to run this app. TypeScript中如何正确声明支持动态推导的getProperty函数
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何正确声明支持动态推导的getProperty函数

TypeScript prop 函数返回值类型推导异常解决方案

问题描述

你需要实现一个通用的属性读取函数prop(propName, obj),希望返回值类型能根据传入的键和对象自动推导,且不需要强制校验传入的键必须是对象的合法键,但自行编写的泛型声明存在推导不一致的问题:

  • 官方提供的基础实现declare function getProperty<T, U extends keyof T>(name: U, obj: T): T[U];会强制校验键名合法性,不符合动态传参需求
  • 自行实现的PropOfObj工具类型单独测试时推导正常,但在函数泛型场景下推导异常:传入未断言的字面量键时返回unknown,只有对键做类型断言才能得到正确的属性类型

复现代码

type PropOfObj<T, U> = U extends keyof T ? T[U] : unknown;
declare function prop<T, U>(propName: U, obj: T): PropOfObj<T, U>;

// 推导异常的场景
const n1 = prop('name', { name: 'David' }); // 预期为string,实际推导为unknown
const n2 = prop('name' as 'name', { name: 'David' }); // 断言后推导正常为string
const n3 = prop('name', { [Symbol('name')]: 'David' }); // 非预期推导为string

// 单独测试工具类型推导正常
const obj = { name: 'David' };
const key = 'name';
type T = typeof obj; // { name: string }
type U = typeof key; // "name"
type Extended = U extends keyof T ? T[U] : unknown; // 推导为string,符合预期

异常核心原因

问题出在函数泛型推导优先级和字符串字面量类型的推导规则上:

  • 当直接传入字符串字面量'name'作为propName参数时,TypeScript默认会把泛型U推导为string宽类型,而非字面量类型'name'
  • 此时传入的对象类型T的键是'name',string无法匹配keyof T的字面量类型,因此条件类型U extends keyof T判断为false,返回unknown
  • 单独测试工具类型时,key变量的类型被显式推导为字面量'name',因此条件类型可以正常匹配

调整后的泛型声明

只需要给泛型U增加键类型基础约束,告知TypeScript推导propName参数类型时优先保留字面量类型,不要放宽为宽类型:

type PropOfObj<T, U> = U extends keyof T ? T[U] : unknown;
// 调整点:为U增加基础约束,要求是可作为对象键的类型范围
declare function prop<T, U extends string | number | symbol>(propName: U, obj: T): PropOfObj<T, U>;

调整后的推导效果

const n1 = prop('name', { name: 'David' }); // 推导为string,符合预期
const n2 = prop('age', { name: 'David' }); // 推导为unknown,符合预期(age不是对象的合法键)
const n3 = prop(Symbol('name'), { [Symbol('name')]: 'David' }); // 推导为string,符合预期

该约束不会强制校验传入的键是否为对象的合法键,仅优化类型推导逻辑:

  • 传入合法键时,返回对应属性的精确类型
  • 传入非法键时,返回unknown,完全满足动态传参的需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.09 11:15:10