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

TypeScript中如何在对象属性含已知前缀时推导正确类型?

TypeScript类型收窄问题:对象联合类型的类型守卫失效

问题场景

先定义以下类型:

type ColumnName<
  Prefix extends string,
  Period extends 'month' | 'week'
> = `${Prefix}.${Period}`;

例如,ColumnName<'dimension_date', 'month'> 会得到 'dimension_date.month'。接着定义:

type Month<Prefix extends string> = ColumnName<Prefix, 'month'>;
type Week<Prefix extends string> = ColumnName<Prefix, 'week'>;

先实现函数get,接受Month<Prefix>或Week<Prefix>类型的column,以及Prefix类型的prefix:

const get = <Prefix extends string>(
  column: Month<Prefix> | Week<Prefix>,
  prefix: Prefix
) => {
    const monthColumn: ColumnName<Prefix, 'month'> = `${prefix}.month`;

    if (column === monthColumn) {
      console.log(column); // 此时column类型为`${Prefix}.month`,符合预期
    }
  };

该函数类型推导正常。但当参数改为对象时:

type MonthObject<Prefix extends string> = { [K in Month<Prefix>]: number };
type WeekObject<Prefix extends string> = { [K in Week<Prefix>]: number };

const getObj = <Prefix extends string>(
  object: MonthObject<Prefix> | WeekObject<Prefix>,
  prefix: Prefix
) => {
  const monthColumn: ColumnName<Prefix, 'month'> = `${prefix}.month`;

  if (monthColumn in object) {
    console.log(object); // 此时object仍为MonthObject<Prefix> | WeekObject<Prefix>,期望仅为MonthObject<Prefix>
  }
}

问题:如何让TypeScript识别if语句内的object类型仅为MonthObject<Prefix>?


解决方案

自定义类型守卫

TypeScript内置的in操作符无法自动收窄泛型联合类型,因为monthColumn是基于泛型推导的动态字符串,而非静态字面量。我们可以编写自定义类型守卫来解决这个问题:

type ColumnName<
  Prefix extends string,
  Period extends 'month' | 'week'
> = `${Prefix}.${Period}`;

type Month<Prefix extends string> = ColumnName<Prefix, 'month'>;
type Week<Prefix extends string> = ColumnName<Prefix, 'week'>;

type MonthObject<Prefix extends string> = { [K in Month<Prefix>]: number };
type WeekObject<Prefix extends string> = { [K in Week<Prefix>]: number };

// 自定义类型守卫函数
function isMonthObject<Prefix extends string>(
  obj: MonthObject<Prefix> | WeekObject<Prefix>,
  monthColumn: ColumnName<Prefix, 'month'>
): obj is MonthObject<Prefix> {
  return monthColumn in obj;
}

const getObj = <Prefix extends string>(
  object: MonthObject<Prefix> | WeekObject<Prefix>,
  prefix: Prefix
) => {
  const monthColumn: ColumnName<Prefix, 'month'> = `${prefix}.month`;

  if (isMonthObject(object, monthColumn)) {
    console.log(object); // 此时object类型自动收窄为MonthObject<Prefix>
  }
}

替代方案:类型断言

如果不需要复用类型判断逻辑,也可以直接在条件分支中使用类型断言:

const getObj = <Prefix extends string>(
  object: MonthObject<Prefix> | WeekObject<Prefix>,
  prefix: Prefix
) => {
  const monthColumn: ColumnName<Prefix, 'month'> = `${prefix}.month`;

  if (monthColumn in object) {
    console.log(object as MonthObject<Prefix>); // 手动断言为目标类型
  }
}

这种方式更简洁,但需要开发者自行保证断言的正确性,不如自定义类型守卫安全可靠。


内容的提问来源于stack exchange,提问作者Luuc van der Zee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 18:50:27