In Angular 18.1 I have the following code:
public unit = model('px');
And it works correctly, indeed if I inspect the type I get:
ModelSignal<string>
I want to provide a better type definition for the model, particularly I want my model to wrap the literal union type 'px' | '%'
. For this reason I wrote:
public unit: ModelSignal<'px' | '%'> = model('px');
However, I receive the following compilation error:
Type ‘ModelSignal<“px”>’ is not assignable to type ‘ModelSignal<“%” |
“px”>’. The types of ‘[SIGNAL].transformFn’ are incompatible between
these types.
Type ‘((value: “px”) => “px”) | undefined’ is not assignable to type ‘((value: “%” | “px”) => “%” | “px”) | undefined’.
Type ‘(value: “px”) => “px”‘ is not assignable to type ‘(value: “%” | “px”) => “%” | “px”‘.
Types of parameters ‘value’ and ‘value’ are incompatible.
Type ‘”%” | “px”‘ is not assignable to type ‘”px”‘.
Type ‘”%”‘ is not assignable to type ‘”px”‘
It looks like there is some problem with the transformFn
type definition. However, giving a glance to the source code, it seems pretty simple to me:
declare interface InputSignalNode<T, TransformT> extends SignalNode<T> {
transformFn: ((value: TransformT) => T) | undefined;
applyValueToInputSignal<T, TransformT>(node: InputSignalNode<T, TransformT>, value: T): void;
}
where T and TransformT, in this case, are the same type:
export declare interface ModelSignal<T> extends WritableSignal<T>, InputSignal<T>, OutputRef<T> {
[SIGNAL]: InputSignalNode<T, T>;
}
So it seems like it’s not able to assign T to T.
I feel like I’m missing something. What’s preventing me from using union types in Angular model signals?
As a workaround I found:
public unit: ModelSignal<Unit> = model('px' as Unit);
But I would like to understand the reason why we need this
Since you are not explicitly specifying the type parameters for the model function it is throwing error.
Try this:
public unit: ModelSignal<'px' | '%'> = model<'px' | '%'>('px');