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

如何定义继承带InputSignal属性抽象类的Angular组件Record?

问题

我尝试定义如下Record类型:

// imports...

export abstract class AbstractComponent<T extends string> {
  abstract prop: InputSignal<T>;
}

export class TestComponent extends AbstractComponent<'test'> {
  prop: InputSignal<'test'> = input('test');
}

@Component({ standalone: true, template: `` })
export class App {
  component: Record<string, typeof AbstractComponent<string>> = {
    'test-1': TestComponent,
  };
}

但出现类型错误:

Type 'typeof TestComponent' is not assignable to type 'typeof AbstractComponent<string>'.
  Construct signature return types 'TestComponent' and 'AbstractComponent<string>' are incompatible.
    The types of 'prop[SIGNAL].transformFn' are incompatible between these types.
      Type '((value: "test") => "test") | undefined' is not assignable to type '((value: string) => string) | undefined'.
        Type '(value: "test") => "test"' is not assignable to type '(value: string) => string'.
          Types of parameters 'value' and 'value' are incompatible.
            Type 'string' is not assignable to type '"test"'.(2322)

实际项目中,泛型T extends string里的T是接口,'test'字面量类型是该接口的子类型。要求Record的值必须类型安全:不能赋值component = { 'a': null },也不能向signal传递非string子类的类型。请问该如何修复这个错误?

修复方案

核心原因

错误源于TypeScript的逆变规则:AbstractComponent<'test'>的prop对应的transformFn仅接受'test'类型参数,而AbstractComponent<string>的transformFn接受任意string类型参数。由于函数参数是逆变的,窄类型的函数无法赋值给宽类型的函数,否则会出现类型安全问题(比如传入非'test'的string到TestComponent的prop)。

方案1:使用satisfies运算符(推荐)

利用TypeScript 4.9+的satisfies运算符,既可以约束component的类型符合要求,又能保留子类型的具体类型信息,同时保证类型安全:

@Component({ standalone: true, template: `` })
export class App {
  component = {
    'test-1': TestComponent,
  } satisfies Record<string, new () => AbstractComponent<string>>;
}
  • 优势:无需修改原有类定义,既限制了component不能赋值null或不符合要求的类型,又保留了TestComponent的具体类型,确保signal只能接受'test'类型值。

方案2:添加泛型逆变修饰符(若允许修改AbstractComponent)

如果可以修改AbstractComponent的泛型定义,给T添加逆变修饰符in,让AbstractComponent<'test'>成为AbstractComponent<string>的子类型:

export abstract class AbstractComponent<in T extends string> {
  abstract prop: InputSignal<T>;
}

@Component({ standalone: true, template: `` })
export class App {
  component: Record<string, typeof AbstractComponent<string>> = {
    'test-1': TestComponent,
  };
}
  • 注意:此方案要求InputSignal<T>的类型也是逆变或双向协变的,否则可能引发其他类型问题。

方案3:类型断言(谨慎使用)

定义通用构造函数类型后,用类型断言绕过类型检查,仅在确定类型安全的场景下使用:

type StringComponentConstructor = new () => AbstractComponent<string>;

@Component({ standalone: true, template: `` })
export class App {
  component: Record<string, StringComponentConstructor> = {
    'test-1': TestComponent as unknown as StringComponentConstructor,
  };
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 13:12:33