如何在TypeScript中实现键值对函数的类型收窄?
问题描述
我想创建一个函数,接收某类型的键及对应取值,处理特定键时执行相应逻辑。实际用在React封装的Table组件里,组件会遍历对象条目并把键值传入单独函数,我需要安全提供自定义覆盖能力。
我简化了代码,定义了User类型和打印用户详情name的函数,但value.name无法被类型收窄,导致访问报错。
代码示例
type User = { details: { name: string; }; account: { status: boolean; } } const printName = <K extends keyof User>(key: K, value: User[K]) => { if (key === 'details') { console.log(value.name); // 无法正常工作 } } const user: User = { details: { name: 'Bob' }, account: { status: true } } printName('details', user.details);
类型错误信息
Property 'name' does not exist on type '{ name: string; } | { status: boolean; }'. Property 'name' does not exist on type '{ status: boolean; }'.(2339)
解决方案
TypeScript无法自动通过key的字面量判断收窄value的类型,因为泛型K是keyof User的子集,条件判断无法直接关联泛型参数的具体类型。以下是几种可行的解决方法:
方法1:类型断言
直接在条件块内将value断言为对应类型,简单直接:
const printName = <K extends keyof User>(key: K, value: User[K]) => { if (key === 'details') { console.log((value as User['details']).name); } }
方法2:函数重载
通过重载签名明确不同键对应的参数类型,让TypeScript正确推断:
type User = { details: { name: string }; account: { status: boolean }; } // 重载签名 function printName(key: 'details', value: User['details']): void; function printName(key: 'account', value: User['account']): void; // 实现签名 function printName(key: keyof User, value: User[keyof User]): void { if (key === 'details') { console.log(value.name); // 类型收窄成功 } } const user: User = { details: { name: 'Bob' }, account: { status: true } } printName('details', user.details);
方法3:自定义类型守卫
编写类型守卫函数关联key和value的类型,适合复杂场景复用:
type User = { details: { name: string }; account: { status: boolean }; } function isDetailsEntry<K extends keyof User>(key: K, value: User[K]): value is User['details'] { return key === 'details'; } const printName = <K extends keyof User>(key: K, value: User[K]) => { if (isDetailsEntry(key, value)) { console.log(value.name); // 类型收窄成功 } } const user: User = { details: { name: 'Bob' }, account: { status: true } } printName('details', user.details);
内容的提问来源于stack exchange,提问作者Sebino Hoo
相关产品推荐
相关产品推荐

