TypeScript中if语句为何会改变signal.write的类型定义?
问题:Signal的write函数中if语句导致类型异常
我正在自主练习学习JS Signals,尝试用TypeScript重写参考代码以加深理解。大部分类型定义已适配,但在signal.write函数中遇到类型问题,错误表现为类型推断异常,代码如下:
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()); })
问题原因
TypeScript通过typeof valueOrFn === "function"只能判断出变量是函数类型,但无法自动推断出这个函数的签名就是(value: T) => T——从类型系统角度看,它可能是任意函数。
当你执行const newValue:T = ...赋值时,TypeScript会检查赋值的安全性,但因为无法确认函数的参数和返回值类型与T匹配,直接调用valueOrFn(_value)会触发潜在的类型错误。而添加if (newValue === _value) return语句后,TypeScript的类型检查上下文发生变化,导致原本的隐性类型问题被明确暴露出来。
修复方案
方案1:显式类型断言
通过类型断言明确函数的签名,让TypeScript确认调用的安全性:
function write(valueOrFn: T | ((value: T) => T)) { const newValue: T = typeof valueOrFn === "function" ? (valueOrFn as (val: T) => T)(_value) : valueOrFn; if (newValue === _value) return; _value = newValue; for (const subscriber of [..._subscribers]) { subscriber.notify(); } }
方案2:自定义类型守卫
更严谨的方式是创建类型守卫函数,明确判断函数的签名:
function isUpdaterFn<T>(val: T | ((val: T) => T)): val is (val: T) => T { return typeof val === "function"; } function write(valueOrFn: T | ((value: T) => T)) { const newValue: T = isUpdaterFn(valueOrFn) ? valueOrFn(_value) : valueOrFn; if (newValue === _value) return; _value = newValue; for (const subscriber of [..._subscribers]) { subscriber.notify(); } }
补充说明
TypeScript的类型系统无法仅通过typeof关键字识别函数的具体参数和返回值类型,因此需要开发者手动辅助类型推断。显式断言或自定义类型守卫能让类型系统明确函数的签名,从而消除类型错误。
内容的提问来源于stack exchange,提问作者Jumscrafteur
相关产品推荐
相关产品推荐

