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

如何基于方法更新TypeScript类内部属性的类型

TypeScript 类方法调用后类型无法动态更新的解决方案

我有一个类,可接收HTMLElement、SVGElement或这两种元素组成的数组作为输入。希望通过强类型实现:类的方法能根据用户输入的内容,正确反映对应类型。初始化时该逻辑正常,但类提供了初始化后添加元素的方法,无法让TypeScript识别到该方法调用后的类型变化。

示例代码:

type InputTypes = HTMLElement | SVGElement

function ensureArray<T>(input?: T | T[]): T[] {
  if(!input) return []
  return Array.isArray(input) ? input : [input];
}

class MyClass2<E extends InputTypes> {
  els?: E | [E]
  bar: E;
  constructor(m1: MyClass<E>) {
    this.els = m1.props.els
    const ensured = ensureArray<E>(this.els)
    this.bar = ensured[0]
  }
}

class MyClass<E extends InputTypes>{
  props: { els?: E | [E] }
  m2: MyClass2<E>
  constructor(props: { els?: E | [E] }) {
    this.props = props
    this.m2 = new MyClass2(this)
  }

  getBar() {
    return this.m2.bar
  }

  public setBar = (els: E | [E]) => {
    const ensured = ensureArray<E>(els)
    this.m2.bar = ensured[0]
  }
}

const element = document.getElementsByClassName("bla")[0]

var test1 = new MyClass({})
var foo1 = test1.getBar()
var bar1 = test1.setBar(element as HTMLElement)
var baz1 = test1.getBar() // Type is InputTypes instead of HTMLElement

var test2 = new MyClass({ els: element as HTMLElement })
var foo2 = test2.getBar()
var baz21 = test2.getBar() // HTMLElement, correct! but…
var bar2 = test2.setBar(element as SVGElement) // Type 'SVGElement' is missing properties from type 'HTMLElement'
var baz22 = test2.getBar()

示例中存在的问题:

  • test1调用setBar传入HTMLElement后,getBar返回类型仍为InputTypes,不符合预期;
  • test2初始化传入HTMLElement后,无法传入SVGElement,且调用后getBar类型也未更新为HTMLElement | SVGElement。

解决方案

TypeScript的静态类型系统无法直接修改已有变量的类型,但可以通过泛型方法返回新类型实例的方式,实现类型的动态追踪。修改后的代码如下:

type InputTypes = HTMLElement | SVGElement

function ensureArray<T>(input?: T | T[]): T[] {
  if (!input) return []
  return Array.isArray(input) ? input : [input];
}

class MyClass2<E extends InputTypes = never> {
  els?: E | E[]
  bar?: E;
  constructor(els?: E | E[]) {
    const ensured = ensureArray<E>(els)
    this.bar = ensured[0]
    this.els = els
  }
}

class MyClass<E extends InputTypes = never> {
  props: { els?: E | E[] }
  m2: MyClass2<E>

  constructor(props: { els?: E | E[] } = {}) {
    this.props = props
    this.m2 = new MyClass2(props.els)
  }

  getBar(): E | undefined {
    return this.m2.bar
  }

  public setBar<NewE extends InputTypes>(els: NewE | NewE[]): MyClass<E | NewE> {
    const newInstance = new MyClass<E | NewE>({
      els: [...ensureArray(this.props.els), ...ensureArray(els)] as (E | NewE)[]
    })
    return newInstance
  }
}

const element = document.getElementsByClassName("bla")[0]

// 测试1:空初始化后添加元素
let test1 = new MyClass({})
let foo1 = test1.getBar() // undefined
test1 = test1.setBar(element as HTMLElement)
let baz1 = test1.getBar() // HTMLElement | undefined

// 测试2:初始化后添加不同类型元素
let test2 = new MyClass({ els: element as HTMLElement })
let foo2 = test2.getBar() // HTMLElement | undefined
test2 = test2.setBar(element as SVGElement)
let baz22 = test2.getBar() // HTMLElement | SVGElement | undefined

关键说明

  1. 泛型累积:MyClass的泛型E代表当前已包含的元素类型,初始默认值为never(表示无元素);
  2. 泛型方法:setBar定义为泛型方法,接受新元素类型NewE,返回MyClass<E | NewE>实例,实现类型的扩展;
  3. 实例替换:由于TypeScript无法修改已有变量的类型,需要将setBar返回的新实例赋值给原变量,以此更新类型;
  4. 可选类型处理:getBar返回E | undefined,明确反映元素可能不存在的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:30:05