如何让TypeScript基于另一函数的非空返回值判断当前返回值非空?
TypeScript类型优化:关联Socket存在性与前台状态判断
问题场景
现有以下代码:
const sockets = new Map<string, { socket: (...args: any[]) => void; isForegrounded: boolean }>(); sockets.set('1', { socket: { emit: () => undefined }, isForegrounded: true }); sockets.set('2', { socket: { emit: () => undefined }, isForegrounded: false }); const getSocket = (socketId: string) => sockets.get(socketId)?.socket ?? null; const getIsForegroundedSocket = (socketId: string) => sockets.get(socketId)?.isForegrounded ?? false; const someFunction = () => { // do stuff const socketId = '1'; const isForegroundedSocket = getIsForegroundedSocket(socketId); if (isForegroundedSocket) { const socket = getSocket(socketId); socket.emit('some-action', { hello: 'world' }); } // do more stuff };
TypeScript会抛出错误:
socket.emit('some-action', { hello: 'world' });
^^^^^^socketis possibly null
但实际逻辑是:如果getIsForegroundedSocket返回true,同一socketId对应的getSocket必然不会返回null。我们需要让TypeScript理解这个关联逻辑,同时避免使用非空断言!。
解决方案
方案1:直接获取完整条目,利用类型窄化
不再拆分两个独立的获取函数,直接获取Map中的完整条目,通过判断条目内的isForegrounded来窄化类型:
const sockets = new Map<string, { socket: (...args: any[]) => void; isForegrounded: boolean }>(); sockets.set('1', { socket: { emit: () => undefined }, isForegrounded: true }); sockets.set('2', { socket: { emit: () => undefined }, isForegrounded: false }); const someFunction = () => { const socketId = '1'; const socketEntry = sockets.get(socketId); if (socketEntry?.isForegrounded) { // TypeScript能识别此时socketEntry必然存在,socket不会为null socketEntry.socket.emit('some-action', { hello: 'world' }); } };
方案2:使用自定义类型保护函数
编写类型谓词函数,明确告诉TypeScript:当函数返回true时,对应的socketId存在有效socket:
const sockets = new Map<string, { socket: (...args: any[]) => void; isForegrounded: boolean }>(); sockets.set('1', { socket: { emit: () => undefined }, isForegrounded: true }); sockets.set('2', { socket: { emit: () => undefined }, isForegrounded: false }); const getSocket = (socketId: string) => sockets.get(socketId)?.socket ?? null; // 自定义类型保护,判断socketId对应条目存在且为前台状态 function isForegroundedSocket(socketId: string): socketId is string { return !!sockets.get(socketId)?.isForegrounded; } const someFunction = () => { const socketId = '1'; if (isForegroundedSocket(socketId)) { const socket = getSocket(socketId); // TypeScript会自动推断socket不为null socket.emit('some-action', { hello: 'world' }); } };
方案3:封装Socket管理逻辑,提供关联查询方法
将Socket的操作封装成统一的管理类,直接提供“获取前台状态Socket”的方法,让返回值类型自然关联存在性:
class SocketManager { private sockets = new Map<string, { socket: { emit: (event: string, data: any) => void }; isForegrounded: boolean }>(); addSocket(socketId: string, socket: { emit: (event: string, data: any) => void }, isForegrounded: boolean) { this.sockets.set(socketId, { socket, isForegrounded }); } // 直接返回前台状态的Socket,否则返回null getForegroundedSocket(socketId: string) { const entry = this.sockets.get(socketId); return entry?.isForegrounded ? entry.socket : null; } } const socketManager = new SocketManager(); socketManager.addSocket('1', { emit: () => undefined }, true); socketManager.addSocket('2', { emit: () => undefined }, false); const someFunction = () => { const socketId = '1'; const socket = socketManager.getForegroundedSocket(socketId); if (socket) { socket.emit('some-action', { hello: 'world' }); } };
内容的提问来源于stack exchange,提问作者Mike K
相关产品推荐
相关产品推荐

