Skip to content

Instantly share code, notes, and snippets.

@endel
Last active July 19, 2019 20:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save endel/d4177a2f07f9f285631ce32ef2b0da57 to your computer and use it in GitHub Desktop.
Save endel/d4177a2f07f9f285631ce32ef2b0da57 to your computer and use it in GitHub Desktop.
Lightweight Strong Typed Signals in TypeScript
import { createSignal } from "./strong-signal"
// declare the signal
const onNumber = createSignal<(num: number) => void>();
// ok
onNumber(function(num) {});
// compilation error! second argument not allowed
onNumber(function(num, secondArg) {});
const onTwoArgs = createSignal<(str: string, num: number) => void>();
onTwoArgs(function(str, num) {
// you get correct "string" auto-completion for "str"
});
// register a callback to run only once
onTwoArgs.once(function(str, num) { console.log("I'm invoked only once!"); })
// compilation error! two arguments are required
onTwoArgs.invoke();
// ok
onTwoArgs.invoke("hello", 42);
type FunctionParameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any
? P
: never;
class EventEmitter<CallbackSignature extends (...args: any[]) => any> {
handlers: Array<CallbackSignature> = [];
register(cb: CallbackSignature, once: boolean = false) {
this.handlers.push(cb);
return this;
}
invoke(...args: FunctionParameters<CallbackSignature>) {
for (let i = 0, l = this.handlers.length; i < l; i++) {
this.handlers[i].apply(null, args);
}
}
remove (cb: CallbackSignature) {
const index = this.handlers.indexOf(cb);
this.handlers[index] = this.handlers[this.handlers.length - 1];
this.handlers.pop();
}
clear() {
this.handlers = [];
}
}
export function createSignal<CallbackSignature extends (...args: any[]) => void>() {
const emitter = new EventEmitter<CallbackSignature>();
function register(this: any, cb: CallbackSignature): EventEmitter<CallbackSignature> {
return emitter.register(cb, this === null);
};
register.once = (cb: CallbackSignature) => {
const callback: any = function (...args: any[]) {
cb(...args);
emitter.remove(callback);
}
emitter.register(callback);
}
register.remove = (cb: CallbackSignature) => emitter.remove(cb)
register.invoke = (...args: FunctionParameters<CallbackSignature>) => emitter.invoke(...args);
return register;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment