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

TypeScript泛型基类与映射类型:初始化及约束错误求助

TypeScript表单系统类型安全实现问题

代码文件内容

AddShiftForm.ts

type AddShiftFormInputDef = {
  "start": BehaviorSubject<{value: Date   , valid: boolean}>,
  "end": BehaviorSubject<{value: Date  , valid: boolean}>,
  "name": BehaviorSubject<{value: string, valid: boolean}>
}

export class AddShiftForm extends AbstractGqlForm<AddShiftFormInputDef>{
  constructor(requiredInputs:RequiredInputs) {
    super(requiredInputs)
  }
}

BaseGqlForm.ts

export abstract class AbstractGqlForm<T extends {[Property in keyof T]: 
BehaviorSubject<GqlFormInputStatus> }>{

  protected inputSubjects: {[Property in keyof T]: BehaviorSubject<any>} | undefined;
  protected requiredInputs: RequiredInputs;
  protected constructor(requiredInputs:RequiredInputs) {
    this.requiredInputs = requiredInputs;
  }

  registerInput(inputName: keyof T, input$: BehaviorSubject<GqlFormInputStatus>) {
    if( this.inputSubjects === undefined){
      this.inputSubjects = {inputName: input$};
    }else{
      this.inputSubjects[inputName] = input$;
    }
  }
}

GqlFormInputStatus.ts

import {BaseGqlInput} from "./BaseGqlInputTypes";

export interface GqlFormInputStatus {
    value: BaseGqlInput | undefined ,
    valid: boolean
}

export interface TypedGqlFormInputStatus<T extends BaseGqlInput> {
    value: T  | undefined,
    valid: boolean
}

BaseGqlInputTypes.ts

export type BaseGqlInput = string | number | Date;

遇到的两个问题

问题1:继承时类型约束不匹配

AddShiftForm继承AbstractGqlForm时触发TypeScript错误:

TS2344: Type 'AddShiftFormInputDef' does not satisfy the constraint '{ start: BehaviorSubject; end: BehaviorSubject; name: BehaviorSubject; }'.
The types of '"start".observers' are incompatible between these types.
Type 'Observer<{ value: Date; valid: boolean; }>[]' is not assignable to type 'Observer[]'.
Type 'Observer<{ value: Date; valid: boolean; }>' is not assignable to type 'Observer'

问题2:inputSubjects初始化与赋值错误

  1. 直接初始化报错:

    protected inputSubjects: {[Property in keyof T]: BehaviorSubject<any>} = {};
    

    TS2322: Type '{}' is not assignable to type '{ [Property in keyof T]: BehaviorSubject; }'.

  2. 在registerInput中初始化时:

    this.inputSubjects = {inputName: input$};
    

    TS2322: Type '{ inputName: BehaviorSubject; }' is not assignable to type '{ [Property in keyof T]: BehaviorSubject; }'.
    Object literal may only specify known properties, and 'inputName' does not exist in type '{ [Property in keyof T]: BehaviorSubject; }'.

解决方案

解决问题1:统一类型约束

问题核心是AddShiftFormInputDef中BehaviorSubject的泛型是具体结构,而基类约束的是GqlFormInputStatus,TypeScript默认不认为结构兼容的匿名类型与接口是同一类型。

修改步骤:

  1. 用TypedGqlFormInputStatus定义具体输入类型,确保与GqlFormInputStatus的继承关系
  2. 调整基类泛型约束,兼容子类型:

修改AbstractGqlForm:

export abstract class AbstractGqlForm<T extends {[K in keyof T]: BehaviorSubject<TypedGqlFormInputStatus<infer V>>} 
  where V extends BaseGqlInput>{

  protected inputSubjects: Partial<{[K in keyof T]: T[K]}> = {};
  protected requiredInputs: RequiredInputs;

  protected constructor(requiredInputs: RequiredInputs) {
    this.requiredInputs = requiredInputs;
  }

  registerInput<K extends keyof T>(inputName: K, input$: T[K]) {
    this.inputSubjects[inputName] = input$;
  }
}

修改AddShiftFormInputDef:

type AddShiftFormInputDef = {
  "start": BehaviorSubject<TypedGqlFormInputStatus<Date>>,
  "end": BehaviorSubject<TypedGqlFormInputStatus<Date>>,
  "name": BehaviorSubject<TypedGqlFormInputStatus<string>>
}

解决问题2:inputSubjects初始化与赋值

  1. 直接初始化:用Partial将属性转为可选,避免空对象不匹配类型的问题,如上面代码所示。
  2. registerInput赋值:原代码错误在于用字面量inputName作为键,需改为计算属性名,配合Partial类型无需额外断言即可直接赋值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 02:45:36