I try to define a Record like this:
// 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,
};
}
And I get this error:
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
generic is an interface and the 'test'
literal type is its sub-type.component = { 'a': null }
or pass a type that doesn't extend string
to the signalHow do I fix the error?
After a whole day of trial, error and confusion i've come up with this:
export class App {
component: Record<string, typeof AbstractComponent<string>> = {
'test-1': TestComponent as typeof AbstractComponent<string>,
};
}
I've also made a more complex demo of why I have so many restrictions and concerns about type safety. It's also available on StackBlitz.
import { Component, InputSignal, input } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
interface MyType<T extends Record<string, any> = Record<string, any>> {
value: T;
}
export abstract class AbstractComponent<T extends MyType> {
abstract prop: InputSignal<T>;
}
type Test1Type = MyType<Test1Value>;
interface Test1Value {
verySpecificData: { a: number; b: boolean; c: null };
}
export class Test1Component extends AbstractComponent<Test1Type> {
readonly prop = input.required<Test1Type>();
readonly data = this.prop().value.verySpecificData;
}
type Test2Type = MyType<Test2Value>;
interface Test2Value {
differentData: { d: string; e: object };
}
export class Test2Component extends AbstractComponent<Test2Type> {
readonly prop = input.required<Test2Type>();
readonly data = this.prop().value.differentData;
}
@Component({ standalone: true, template: `` })
export class App {
components: Record<string, typeof AbstractComponent<MyType>> = {
'test-1': Test1Component as typeof AbstractComponent,
'test-2': Test2Component as typeof AbstractComponent,
};
}
bootstrapApplication(App);