如何定义继承带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
相关产品推荐
相关产品推荐

