Skip to content

Commit

Permalink
Add type arithmetic to improve event type-safety (#705)
Browse files Browse the repository at this point in the history
This PR just adds some utility types that improve type-safety on event types. They are currently not used, but I figured that it was worth including it in a separate PR since it is rather dense code that takes some time to get used to.

### Test Plan

Example of it in action:
```ts
const eventData: AnyEvent<BatchExchange> = {} as any
switch (eventData.event) {
case "Token":                              // ERR
  break
case "OrderPlacement":
  eventData.returnValues.buyToken = "asdf" // OK
  break
case "Withdraw":
  eventData.returnValues.buyToken = "asdf" // ERR
  break
}
```

Produces the folling `tsc` errors:
```
src/streamed/events.ts(50,8): error TS2678: Type '"Token"' is not comparable to type '"OrderPlacement" | "TokenListing" | "OrderCancellation" | "OrderDeletion" | "Trade" | "TradeReversion" |
 "SolutionSubmission" | "Deposit" | "WithdrawRequest" | "Withdraw"'.
src/streamed/events.ts(56,28): error TS2339: Property 'buyToken' does not exist on type '{ user: string; token: string; amount: string; 0: string; 1: string; 2: string; }'.
```
  • Loading branch information
nlordell authored May 4, 2020
1 parent ef67fc1 commit 1213bd5
Showing 1 changed file with 43 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/streamed/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Contract, EventData } from "web3-eth-contract"
import { ContractEvent } from "../../build/types/types"

/**
* Event type specified by name.
*/
type Event<
C extends Contract,
T extends Exclude<keyof C["events"], "allEvents">,
> = EventMetadata & EventDiscriminant<C, T>

/**
* Concrete event type with known properties based on the event name.
*
* @remarks
* This type definition allows the TypeScript compiler to determine the type of
* the `returnValues` property based on `event` property checks. For example:
* ```
* const eventData: AnyEvent<BatchExchange> = ...
* case "Token": // ERR: 2678: Type '"Token"' is not comparable to type
* // '"OrderPlacement" | "TokenListing" | ...'
* break
* case "OrderPlacement":
* eventData.returnValues.buyToken = "asdf" // OK
* break
* case "Withdraw":
* eventData.returnValues.buyToken = "asdf" // ERR: 2339: Property 'buyToken' does not exist on type
* // '{ user: string; token: string; amount: string; ... }'.
* break
* }
* ```
*/
type AnyEvent<C extends Contract> = EventMetadata & EventDiscriminant<C, Exclude<keyof C["events"], "allEvents">>

type EventMetadata = Omit<EventData, "event" | "returnValues">
type EventValues<T> = T extends ContractEvent<infer U> ? U : never
type EventDiscriminant<
C extends Contract,
T extends Exclude<keyof C["events"], "allEvents">,
> = T extends any ? {
event: T,
returnValues: EventValues<C["events"][T]>,
} : never

0 comments on commit 1213bd5

Please sign in to comment.