IFrameMessageResponse is currently defined with a generic parameter TAction, which is constrained as a subtype of IFrameMessageActions. This parameter doesn't usefully narrow or widen the type, and causes two problems:
Type errors on assignment operations that should be valid.
e.g. as type casting: failing because it's trying to assign a supertype to a subtype i.e. (response: IFrameMessageResponse<TAction>) => void to (response: IFrameMessageResponse<IFrameMessageActions>) => void.
In general, (x: SuperType) => void is a subtype of (x: SubType) => void.
This error indicates an underlying issue with the typing and shouldn't be silenced.
Removing TAction puts (response: SomeSpecificIFrameMessageResponse) => void on the LHS (assignee, supertype) and (response: IFrameMessageResponse) => void on the RHS (assigned, subtype), resolving this logical error.
TAction also interferes with type narrowing based on action value, and is silencing type errors. Some union members of IFrameMessageResponse do not include a success, error, payload, or payload.error property, but because of the action: TAction property, TypeScript doesn't alert us that we should be performing in checks on them in addition to null checks. This can cause some of the existing destructuring expressions to unexpectedly fail at runtime.
Constraining IFrameMessageResponse['action'] to IFrameMessageActions both resolves these issues and guides us towards writing type-safe logic about these actions that conform to their specific type signatures. It appears that this was the original intention of writing IFrameMessageActions as a discriminated union instead of a wider type encompassing all of the actions.
Tasks
To resolve these issues, This make IFrameMessageResponse non-generic and removes as casting.
For improved readability and maintainability, refactor IFrameMessageResponse into a union of named types.
Define a IFrameMessageResponseBase type for modularity and better visibility of IFrameMessageResponse types with atypical shapes.
IFrameMessageResponse
is currently defined with a generic parameterTAction
, which is constrained as a subtype ofIFrameMessageActions
. This parameter doesn't usefully narrow or widen the type, and causes two problems:Type errors on assignment operations that should be valid.
as
type casting: failing because it's trying to assign a supertype to a subtype i.e.(response: IFrameMessageResponse<TAction>) => void
to(response: IFrameMessageResponse<IFrameMessageActions>) => void
.(x: SuperType) => void
is a subtype of(x: SubType) => void
.TAction
puts(response: SomeSpecificIFrameMessageResponse) => void
on the LHS (assignee, supertype) and(response: IFrameMessageResponse) => void
on the RHS (assigned, subtype), resolving this logical error.TAction
also interferes with type narrowing based onaction
value, and is silencing type errors. Some union members ofIFrameMessageResponse
do not include asuccess
,error
,payload
, orpayload.error
property, but because of theaction: TAction
property, TypeScript doesn't alert us that we should be performingin
checks on them in addition to null checks. This can cause some of the existing destructuring expressions to unexpectedly fail at runtime.Constraining
IFrameMessageResponse['action']
toIFrameMessageActions
both resolves these issues and guides us towards writing type-safe logic about these actions that conform to their specific type signatures. It appears that this was the original intention of writingIFrameMessageActions
as a discriminated union instead of a wider type encompassing all of the actions.Tasks
IFrameMessageResponse
non-generic and removesas
casting.IFrameMessageResponse
into a union of named types.IFrameMessageResponseBase
type for modularity and better visibility ofIFrameMessageResponse
types with atypical shapes.