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

如何用TypeScript类型限制对象重复赋值并关联枚举键与属性

TypeScript 类型约束与重复赋值拦截方案

问题场景

现有一段基于Mobx的TypeScript代码,存在以下问题:

  • 可重复给systemColumns[SystemColumnsName.STATUS]赋值相同枚举键的SystemColumn实例,编译器无报错
  • systemColumns的枚举键与对应SystemColumn实例的name参数无强关联,可能出现不匹配的情况

原代码如下:

import {makeAutoObservable, makeObservable, runInAction} from "mobx";
import {MainStore} from "./MainStore";

export const enum SystemColumnsName {
    STATUS = 'Статус',
}
type SystemColumnsType = {
    [key: string]: SystemColumn;
};

class SystemColumn {
    readonly id: string;
    readonly name: string;
    readonly referencePath: string;
    public isActive: boolean;
    constructor(item_key: string, name: string, referencePath: string, isActive: boolean) {
        makeAutoObservable(this)
        this.id = item_key;
        this.name = name;
        this.referencePath = referencePath;
        this.isActive = isActive;
    }
}


export class ViewStore {
    public systemColumns: SystemColumnsType = {};
    private _mainStore: MainStore;

    constructor(mainStore: MainStore) {
        makeAutoObservable(this)
        this._mainStore = mainStore;
        this.systemColumns[SystemColumnsName.STATUS] = new SystemColumn('systemsStatusItem', SystemColumnsName.STATUS, 'statusColumn', true);
        this.systemColumns[SystemColumnsName.STATUS] = new SystemColumn('systemsStatusItem', SystemColumnsName.STATUS, 'statusColumn', true);
        this.systemColumns[SystemColumnsName.STATUS] = new SystemColumn('systemsStatusItem', SystemColumnsName.STATUS, 'statusColumn', true);
        this.systemColumns[SystemColumnsName.STATUS] = new SystemColumn('systemsStatusItem', SystemColumnsName.STATUS, 'statusColumn', true);
        this.systemColumns[SystemColumnsName.STATUS] = new SystemColumn('systemsStatusItem', SystemColumnsName.STATUS, 'statusColumn', true);
    }
    public setSystemColumnStatus(systemColumnKey: string, isActive: boolean) {
        this.systemColumns[systemColumnKey].isActive = isActive;
    }
}

需要实现三个目标:

  1. 禁止重复给同一枚举键赋值SystemColumn实例
  2. 建立枚举键与对应SystemColumn实例name参数的强关联
  3. 重复赋值时强制编译器抛出错误

解决方案

1. 泛型约束SystemColumn的name参数

通过泛型让SystemColumn的name属性严格对应SystemColumnsName的枚举成员,确保两者一致:

// 为每个枚举成员定义专属的SystemColumn类型映射
type SystemColumnMap = {
  [K in SystemColumnsName]: SystemColumn<{ name: K }>;
};

// 用泛型约束name参数必须是指定的枚举成员
class SystemColumn<T extends { name: SystemColumnsName }> {
  readonly id: string;
  readonly name: T['name'];
  readonly referencePath: string;
  public isActive: boolean;

  constructor(item_key: string, name: T['name'], referencePath: string, isActive: boolean) {
    makeAutoObservable(this);
    this.id = item_key;
    this.name = name;
    this.referencePath = referencePath;
    this.isActive = isActive;
  }
}

2. 拦截重复赋值的泛型添加方法

使用Partial<SystemColumnMap>初始化systemColumns,并通过泛型方法addSystemColumn确保每次添加的枚举键都是未定义过的:

export class ViewStore {
  // 初始为Partial类型,逐步填充为完整的SystemColumnMap
  public systemColumns: Partial<SystemColumnMap> = {};
  private _mainStore: MainStore;

  constructor(mainStore: MainStore) {
    makeAutoObservable(this);
    this._mainStore = mainStore;
    
    // 合法添加列
    this.addSystemColumn(SystemColumnsName.STATUS, 'systemsStatusItem', 'statusColumn', true);
    
    // 以下代码会触发编译错误:无法将类型"SystemColumnsName.STATUS"分配给类型"Exclude<SystemColumnsName, SystemColumnsName.STATUS>"
    // this.addSystemColumn(SystemColumnsName.STATUS, 'systemsStatusItem', 'statusColumn', true);
  }

  // 泛型方法:仅允许添加systemColumns中尚未存在的枚举键
  private addSystemColumn<K extends Exclude<SystemColumnsName, keyof typeof this.systemColumns>>(
    columnName: K,
    itemKey: string,
    referencePath: string,
    isActive: boolean
  ) {
    this.systemColumns[columnName] = new SystemColumn<{ name: K }>(itemKey, columnName, referencePath, isActive) as SystemColumnMap[K];
  }

  // 约束set方法的参数为合法枚举成员
  public setSystemColumnStatus<K extends SystemColumnsName>(systemColumnKey: K, isActive: boolean) {
    const column = this.systemColumns[systemColumnKey];
    if (column) {
      column.isActive = isActive;
    }
  }
}

3. 完整代码验证

整合后的完整代码如下,此时重复添加同一枚举键会直接触发编译错误,且SystemColumn的name参数必须与枚举键严格匹配:

import { makeAutoObservable } from "mobx";
import { MainStore } from "./MainStore";

export const enum SystemColumnsName {
  STATUS = 'Статус',
}

type SystemColumnMap = {
  [K in SystemColumnsName]: SystemColumn<{ name: K }>;
};

class SystemColumn<T extends { name: SystemColumnsName }> {
  readonly id: string;
  readonly name: T['name'];
  readonly referencePath: string;
  public isActive: boolean;

  constructor(item_key: string, name: T['name'], referencePath: string, isActive: boolean) {
    makeAutoObservable(this);
    this.id = item_key;
    this.name = name;
    this.referencePath = referencePath;
    this.isActive = isActive;
  }
}

export class ViewStore {
  public systemColumns: Partial<SystemColumnMap> = {};
  private _mainStore: MainStore;

  constructor(mainStore: MainStore) {
    makeAutoObservable(this);
    this._mainStore = mainStore;
    
    this.addSystemColumn(SystemColumnsName.STATUS, 'systemsStatusItem', 'statusColumn', true);
    
    // 重复添加会报错
    // this.addSystemColumn(SystemColumnsName.STATUS, 'systemsStatusItem', 'statusColumn', true);
  }

  private addSystemColumn<K extends Exclude<SystemColumnsName, keyof typeof this.systemColumns>>(
    columnName: K,
    itemKey: string,
    referencePath: string,
    isActive: boolean
  ) {
    this.systemColumns[columnName] = new SystemColumn<{ name: K }>(itemKey, columnName, referencePath, isActive) as SystemColumnMap[K];
  }

  public setSystemColumnStatus<K extends SystemColumnsName>(systemColumnKey: K, isActive: boolean) {
    const column = this.systemColumns[systemColumnKey];
    if (column) {
      column.isActive = isActive;
    }
  }
}

内容的提问来源于stack exchange,提问作者Георгий Барсегян

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:35:29