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

如何让TypeScript类型系统在该示例中统一关联表类型

解决Zapatos库中表类型与对应_source类型的关联转换问题

问题场景

使用Zapatos库建模数据库表,现有table_b、table_c及其对应table_b_source、table_c_source的类型定义,需要实现Thing类的convert函数:接收Subset(table_b/table_c)对应的Selectable对象和common字符串,返回对应${T}_source的Selectable类型。但当前代码出现类型不匹配报错,编译器无法识别T与${T}_source的关联关系。

库固定定义代码

// this cannot change -- this is how the library (zapatos in this case) defines things

namespace table_a {
  export type Table = 'table_a';
  export interface Selectable {
    random1: number;
  }
}

namespace table_b {
  export type Table = 'table_b';
  export interface Selectable {
    field1: number;
  }
}

namespace table_c {
  export type Table = 'table_c';
  export interface Selectable {
    field2: number;
  }
}

namespace table_b_source {
  export type Table = 'table_b_source';
  export interface Selectable {
    field1: number;
    common: string;
  }
}

namespace table_c_source {
  export type Table = 'table_c_source';
  export interface Selectable {
    field2: number;
    common: string;
  }
}

type Table =
  | table_a.Table
  | table_b.Table
  | table_c.Table
  | table_b_source.Table
  | table_c_source.Table;

type SelectableForTable<T extends Table> = {
  table_a: table_a.Selectable;
  table_b: table_b.Selectable;
  table_c: table_c.Selectable;
  table_b_source: table_b_source.Selectable;
  table_c_source: table_c_source.Selectable;
}[T];

待实现代码及报错

// This we have more flexibility with
type Subset = Extract<Table, 'table_b' | 'table_c'>;
class Thing<T extends Subset> {
  // how can I get this to work?
  convert(obj: SelectableForTable<T>, common: string): SelectableForTable<`${T}_source`> {
    return {
      ...obj,
      common,
    };
  }
}

报错信息:

Type 'SelectableForTable<T> & { common: string; }' is not assignable to type 'SelectableForTable<`${T}_source`>'.
  Type 'SelectableForTable<T> & { common: string; }' is not assignable to type 'table_b_source.Selectable & table_c_source.Selectable'.
    Type 'SelectableForTable<T> & { common: string; }' is not assignable to type 'Selectable'

解决方案

核心思路是建立Subset类型与对应_source表类型的明确映射,让编译器能清晰识别两者的关联,而非依赖模板字符串的自动推断(TypeScript泛型中模板字符串类型的关联推断能力有限)。

步骤1:创建类型映射表

定义一个映射类型,明确每个Subset表对应的_source表类型:

// 建立Subset表到对应_source表的明确映射
type SourceTableMap = {
  table_b: table_b_source.Table;
  table_c: table_c_source.Table;
};

步骤2:修改Thing类实现

基于映射表重构Thing类,用映射后的类型约束返回值,并通过类型断言让编译器确认转换的安全性(因为我们明确知道扩展后的对象结构与目标Selectable类型完全匹配):

// 用映射表的键重新定义Subset,确保类型关联准确
type Subset = keyof SourceTableMap;

// 可选:封装Source表对应的Selectable类型,简化代码
type SourceSelectable<T extends Subset> = SelectableForTable<SourceTableMap[T]>;

class Thing<T extends Subset> {
  convert(obj: SelectableForTable<T>, common: string): SourceSelectable<T> {
    return {
      ...obj,
      common,
    } as SourceSelectable<T>;
  }
}

原理说明

  • 原代码中${T}_source是模板字符串生成的计算类型,编译器无法自动将其与SelectableForTable中的键建立精确关联,也无法确认交叉类型SelectableForTable<T> & { common: string }与目标Selectable接口的兼容性。
  • 通过SourceTableMap的硬绑定,我们给编译器提供了明确的类型关联规则,再结合类型断言(安全的,因为我们清楚业务逻辑上的结构匹配),就能解决类型不匹配的问题。

内容的提问来源于stack exchange,提问作者A Question Asker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 11:20:54