You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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' });
^^^^^^ socket is 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:04:56