I'm working on a self-guided exercise to learn about JS signals.
I'm following this blog post, but I'm trying to rewrite the code in TypeScript to better understand it.
While I've managed to get the code running with most of the type definitions, I'm facing an issue with the signal.write
function.
Here's a screenshot of the error I'm encountering:
type Observer = { notify: () => void; link: (unlink: any) => void; }
type Signal = <T>(value:T) => [() => T, (value: T | ((value: T) => T)) => void]
let activeObserver: Observer | null = null
const signal:Signal = <T>(value:T) => {
let _value: T = value
const _subscribers: Set<Observer> = new Set()
function unlink(dep:Observer) {
_subscribers.delete(dep)
}
function read() {
if (activeObserver && !_subscribers.has(activeObserver)) {
_subscribers.add(activeObserver)
activeObserver.link(unlink)
}
return _value
}
function write(valueOrFn: T | ((value: T) => T)) {
const newValue:T = typeof valueOrFn === "function" ? valueOrFn(_value) : valueOrFn
if (newValue === _value) return
_value = newValue
for (const subscriber of [..._subscribers]) {
subscriber.notify()
}
}
return [read, write]
}
const effect = (cb: () => unknown) => {
let _externalCleanup: unknown // defined explicitly by user
let _unlinkSubscriptions: Set<(dep:Observer) => void> = new Set() // track active signals (to unlink on re-run)
const effectInstance = { notify: execute, link }
function link(unlink: (dep:Observer) => void) {
_unlinkSubscriptions.add(unlink)
}
function execute() {
dispose()
activeObserver = effectInstance
_externalCleanup = cb()
activeObserver = null
}
function dispose() {
if (typeof _externalCleanup === "function") {
_externalCleanup()
}
}
execute()
return dispose
}
let [count, setCount] = signal(0); // state value set to 0
setInterval(
() => {
setCount(prev => prev + 1)
},
1000
);
effect(() => {
console.log('Count has changed!', count());
})
You have T
as any
when narrowing the union you have any & Function
which TS can't determine whether it's callable. If you for example change to T extend string
(to a non function type). TS would infer string & Function
as never
and properly discards this union member, leaving only the other callable member. I guess you should rethink what T
could be in your code.