Open harrysolovay opened 5 months ago
I know :/ #664
I couldn't make type-safe this.emitEvent()
work. Curious if you can, but it seems TS is just not able to use the this['events']
type.
As the solution I thought we could create events and methods for them at the same time, i.e.
class extends SmartContract {
events = Events({ Updated: { initial: UInt64, final: UInt64 });
}
// in a method:
this.events.emit("Updated", ...) // properly typed
But that must be done very soon if we don't want to wait until the next major version
I prefer the approach you suggested:
class extends SmartContract { events = Events({ Updated: { initial: UInt64, final: UInt64 }); }
This relieves developers of needing to remember readonly
/const
modifiers on the event definition record. One could create a member named events
that has nothing to do with contract events.
Another question: how do we get typed retrieval of events? Continuing off of the example from above...
const counter = new Counter(deployedPk)
const events = await counter.fetchEvents()
events.forEach((event) => {
event.type // `string`
event.event.data // `any`
})
The data contained within the event
is of type any
. Perhaps we could deprecate fetchEvents
and instead reference fetch
on the Events
-factory-initialized member.
class extends Counter extends SmartContract {
events = Events({ Updated: { initial: UInt64, final: UInt64 } });
}
const counter = new Counter(deployedPk)
const events = await counter.events.fetch()
events.forEach((event) => {
event.type // `"Updated"`
event.event.data // `{ initial: UInt64; final: UInt64 }`
})
This API has the added benefit of looking similar to that of state retrieval.
Perhaps fetch
can accept an event name(s) as well, to save bandwidth in the (common) case that the developer only wants a particular event.
const updateEvents = await counter.events.fetch("Update")
const updateAndIncrementEvents = await counter.events.fetch("Update", "Increment")
const updateAndDecrementEvents = await counter.events.fetch("Update", "Decrement")
Or if we really care about keeping event retrieval similar to that of state, events
could be a callable object.
const updateEvents = await counter.events("Update").fetch()
Some questions about the signature of
SmartContract.emitEvent
...Although
SmartContract.emitEvent
is generic over event name, it is not generic over the event data. Is there a particular reason for this?Let's dig deeper with an example: a simple
Counter
contract, with two event variants:Updated
andIncremented
.Within the
increment
method, we emit two events:Updated
andIncremented
. If we supply invalid event data, there is no type error.A secondary consequence of the lack of generic typing of event data is we miss out on completions and key-bound tsdocs offered by the language service.
Another question: in cases where there is no event data, can the second
emitEvent
argument be omitted?Currently, this causes a type error.
A related idea/question (perhaps for another issue). I'm curious if the following API was considered?