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

如何让TypeScript在值类型为多接口联合时正确推断类型?

TypeScript联合类型属性访问的类型推断问题

问题场景

定义了两个接口及包含它们的联合类型:

interface KeyA {
    a: ValueA;
}

interface KeyB {
    b: ValueB;
}

type KeyExpr = KeyA | KeyB | Record<string, never>;

需求是访问KeyExpr实例的a或b属性时,能得到对应的ValueA或ValueB类型,但当前代码中属性访问的类型推断不符合预期:

function func(keyExpr: KeyExpr) {
    const value = keyExpr['a']; 
    // 当前value的类型为never,而非预期的ValueA
}

已通过工具类型KnownKeys获取到联合类型的有效键类型"a" | "b",但直接用该类型访问属性时,类型仍被解析为never:

export type KnownKeys<T> = {
    [K in keyof T]: string extends K ? never : number extends K ? never : K;
} extends infer R
    ? R extends { [_ in keyof T]: infer U }
        ? U
        : never
    : never;

type ValidKeys = KnownKeys<KeyExpr>; // 类型为"a" | "b"

解决方案

1. 用类型守卫缩小类型范围

通过自定义类型守卫函数,判断实例具体属于哪个接口,在守卫后的代码块中访问属性即可得到正确类型:

function isKeyA(obj: KeyExpr): obj is KeyA {
    return 'a' in obj;
}

function isKeyB(obj: KeyExpr): obj is KeyB {
    return 'b' in obj;
}

function func(keyExpr: KeyExpr) {
    if (isKeyA(keyExpr)) {
        const value = keyExpr.a; // 类型为ValueA
    } else if (isKeyB(keyExpr)) {
        const value = keyExpr.b; // 类型为ValueB
    }
}

2. 替换Record<string, never>为更严格的空类型

Record<string, never>会导致联合类型的属性访问被推断为never,可将其替换为明确的空接口或{},避免干扰:

interface EmptyObject {}
type KeyExpr = KeyA | KeyB | EmptyObject;

// 此时访问keyExpr['a']的类型为ValueA | undefined
function func(keyExpr: KeyExpr) {
    if ('a' in keyExpr) {
        const value = keyExpr['a']; // 类型为ValueA
    }
}

3. 工具函数结合条件类型实现类型安全访问

定义工具类型提取对应键的类型,再通过工具函数封装访问逻辑,配合类型断言辅助推断:

type ValueForKey<T, K extends KnownKeys<T>> = 
  T extends { [key in K]: infer V } ? V : never;

function getValue<K extends KnownKeys<KeyExpr>>(
  keyExpr: KeyExpr, 
  key: K
): ValueForKey<KeyExpr, K> {
    return keyExpr[key] as ValueForKey<KeyExpr, K>;
}

// 使用示例
const expr: KeyExpr = { a: {} as ValueA };
const valueA = getValue(expr, 'a'); // 类型为ValueA
const valueB = getValue(expr, 'b'); // 类型为ValueB

原因说明

原问题中Record<string, never>的所有属性访问结果均为never,当联合类型中包含该类型时,TypeScript会对所有成员的索引结果取联合,最终导致keyExpr['a']的类型被推断为ValueA | undefined | never,而never会被联合类型吸收,但实际场景中因类型解析逻辑,最终表现为never。通过上述方法规避Record<string, never>的干扰或缩小类型范围,即可得到正确的类型推断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:10:50