Compare commits

..

No commits in common. "66b656ada5433c0aec3e9e2229447c25d8da43cf" and "f2bf2fb0258e09dfa62d2b6cfb60d4ee4f1a571e" have entirely different histories.

View File

@ -1,6 +1,7 @@
--- ---
title: "Type-Safe Event Emitter in TypeScript" title: "Type-Safe Event Emitter in TypeScript"
date: 2021-09-04T17:18:49-07:00 date: 2021-09-04T17:18:49-07:00
draft: true
tags: ["TypeScript"] tags: ["TypeScript"]
--- ---
@ -24,8 +25,8 @@ We can even write some code to test that this works (just to ease my nerves):
As expected, we get: As expected, we get:
``` ```
Ended!
Started! Started!
Ended!
``` ```
As you probably guessed, we're going to build on this problem a little bit. As you probably guessed, we're going to build on this problem a little bit.
@ -36,7 +37,7 @@ may want to know the number's new value. In JavaScript, this is a trivial change
{{< codelines "JavaScript" "typescript-emitter/js2.js" 1 17 "hl_lines = 6-8" >}} {{< codelines "JavaScript" "typescript-emitter/js2.js" 1 17 "hl_lines = 6-8" >}}
That's literally it. Once again, let's ensure that this works by sending two new events: That's literally it. Once again, let's ensure that this works by sending two new events:
`stringChange` and `numberChange`. `stringChanged` and `numberChanged`.
{{< codelines "JavaScript" "typescript-emitter/js2.js" 19 23 >}} {{< codelines "JavaScript" "typescript-emitter/js2.js" 19 23 >}}
@ -86,7 +87,7 @@ Let's go through each one of them step-by-step.
to limit the possible names to which handlers can be assigned. We limit these names to limit the possible names to which handlers can be assigned. We limit these names
to the keys of our type `T`; in the preceding example, `keyof T` would be `"mouseClick"`. to the keys of our type `T`; in the preceding example, `keyof T` would be `"mouseClick"`.
* We also limit the values: `T[eventName]` retrieves the type of the value associated with * We also limit the values: `T[eventName]` retrieves the type of the value associated with
key `eventName`. In the mouse example, this type would be `{ x: number, y: number }`. We require key `eventName`. In mouse example, this type would be `{ x: number, y: number }`. We require
that a key can only be associated with an array of functions to void, each of which accepts that a key can only be associated with an array of functions to void, each of which accepts
`T[K]` as first argument. This is precisely what we want; for example, `mouseClick` would map to `T[K]` as first argument. This is precisely what we want; for example, `mouseClick` would map to
an array of functions that accept the mouse click location. an array of functions that accept the mouse click location.
@ -98,7 +99,7 @@ Let's go through each one of them step-by-step.
gives TypeScript enough information to narrow the type of `value`. gives TypeScript enough information to narrow the type of `value`.
* __Line 12__: We use the exact same trick here as we did on line 8. * __Line 12__: We use the exact same trick here as we did on line 8.
Let's give this a spin with our `numberChange`/`stringChange` example from earlier: Let's give this a spin with our `numberChanged`/`stringChanged` example from earlier:
{{< codelines "TypeScript" "typescript-emitter/ts.ts" 21 27 >}} {{< codelines "TypeScript" "typescript-emitter/ts.ts" 21 27 >}}
@ -116,5 +117,4 @@ code/typescript-emitter/ts.ts:27:30 - error TS2345: Argument of type 'number' is
Found 2 errors. Found 2 errors.
``` ```
And there you have it! This approach is now also in use in [Hydrogen](https://github.com/vector-im/hydrogen-web), And there you have it!
a lightweight chat client for the [Matrix](https://matrix.org/) protocol. In particular, check out [`EventEmitter.ts`](https://github.com/vector-im/hydrogen-web/blob/master/src/utils/EventEmitter.ts).