Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix contradictory typing of
IFrameMessageResponse
and refactor into…
… modular types (#207) ## Motivation `IFrameMessageResponse` is currently defined with a generic parameter `TAction`. This parameter doesn't usefully narrow or widen the type. - The `action: TAction` property appears to *widen* `IFrameMessageResponse` to accept any action type, but `TAction` is also constrained as a subtype of `IFrameMessageActions`. - The `TAction` generic param appears to *narrow* `IFrameMessageResponse` to select individual union members, but it interferes with the inference-based type narrowing we can get if `IFrameMessageResponse` is an exhaustively enumerated discriminated union. This causes two problems: 1. Type errors on assignment operations that should be valid. - e.g. [`as` type casting](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/207/files#diff-c901ebd8641651c9156a86894574a6d9567f2ae79495de0edff2cbef8ae958f0L302-L304) is necessary here because supertype is being assigned to a subtype i.e. `(response: IFrameMessageResponse<TAction>) => void` to `(response: IFrameMessageResponse<IFrameMessageActions>) => void`. - In general, `(x: SuperType) => T` is a *subtype* of `(x: SubType) => T`. - **This error indicates an underlying issue with the typing and shouldn't be suppressed.** - Removing `TAction` puts `(response: SomeIFrameMessageResponse) => void` on the LHS (assignee, supertype) and `(response: IFrameMessageResponse) => void` on the RHS (assigned, subtype), resolving this logical error. 2. `TAction` is also silencing type errors we should be getting from type narrowing based on `action` value. - e.g. Some union members of `IFrameMessageResponse` do not include a `success`, `error`, `payload`, or `payload.error` property, but because of `TAction`, TypeScript doesn't alert us that we should be performing `in` checks on them in addition to null checks. - This can cause unexpected failure at runtime, especially from the destructuring assignments that are being used. 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 possible actions. ## Changes - **BREAKING** Narrows `IFrameMessageResponse` type definition ## Explanation - To resolve these issues, This PR makes `IFrameMessageResponse` non-generic and removes `as` casting. - For improved readability and maintainability, `IFrameMessageResponse` is also refactored into a union of named types. - A `IFrameMessageResponseBase` type is defined for modularity and better visibility of `IFrameMessageResponse` types with atypical shapes. ## References - Closes #208 - More info on function subtyping: MetaMask/core#1890 (comment)
- Loading branch information