#Undefined type being added to variable that isn't undefined

48 messages · Page 1 of 1 (latest)

violet basin
#

I have this:

        const connection: WebSocketServerPacketSubscription = serverConnection?.subscribeToServerPacket("newChatMessageSent", (packet) => {
            packet = packet as NewChatMessageSentPacket;
            dispatch(addChatMessage(packet.message));
        });

But it's saying that a type of WebSocketServerPacketSubscription | undefined can't be put into the variable, but the function doesn't return undefined, so where's it coming from?

vocal mesa
#

The serverConnection?., if serverConnection is undefined then the result will be undefined.

violet basin
#

Oh

#

And this goes all the way up the chain to my state hook which holds WebSocketConnection | null

#

Is there any way to avoid that so I don't have to put null checks absolutely everywhere

vocal mesa
#

Check serverConnection before hand and handle the case that it's undefined.

violet basin
#

Oh I mean like where I have useState<WebSocketConnection | null>(null), it's basically guarunteed that it won't be null because nothing functions otherwise, but I have to default initialize it to something

vocal mesa
#

I mean, you can use ! to suppress it, but tbh it's not much effort to just write:

if (!serverConnection) return
violet basin
#

Oh, I can initialize when I make the hook apparently, then it's not an issue

#
    const [serverConnection, _] = useState<WebSocketConnection>(() => { // eslint-disable-line @typescript-eslint/no-unused-vars
        const webSocketConn = new WebSocketConnection("http://localhost:8080");
        return webSocketConn;
    });

Now it doesn't need to be null

vocal mesa
#

I'm not sure what that no-unused-vars suppression is for, is that for the _ in const [serverConnection, _]?

violet basin
#

Yea

vocal mesa
#

You can just do const [serverConnection] = ....

violet basin
#

Oh

#

I wish my linter auto recognized variables named _ as discard, but oh well

vocal mesa
#

Pretty sure TS ESLint does recognize it, but this case you can just omit it completely.

#

The lint error still helped you to remove that unnecessary code.

violet basin
#

I forgot that JS allows you to pass values to parameters that don't exist lol

#

Like this does have a value

#

But I can just remove it and it works fine

#

Like what

vocal mesa
#

Yes, you are allowed to not care about an argument.

#

Just remove it completely.

violet basin
#

JS is wild lol

#

Thanks

#

!close

vocal mesa
#

Yeah, I mean "I don't care about the arguments so I can just write () => ..." is so much better than "I don't care about the arguments but I still have to ceremoniously write (_, _, _) => ...."

violet basin
#

Huh, apparently not initializing serverConnection in the useEffect section is having serious ramifications somehow (like the connection exists but doesn't work)

#

So I guess I'm going back to null checks everywhere :/

vocal mesa
#

Wdym by everywhere?

#

If you have a null guard at the top of useEffect, you rest of the code don't need to null check anymore.

violet basin
#

serverConnection is passed down to child components which all need the connection

#

Wait, I have Redux set up. Why am I not storing it in there?

#

My brain is the size of Kansas

#

Wait, that may be the wrong use of Redux, to store what's effectively a global

#

Wait, it doesn't even need to be state, it never changes

#

Dang, for some reason it silently breaks if I initialize it outside of useEffect

vocal mesa
#

I don't use React, but the logic should be pretty similar regardless of framework. If a child component cannot function with a prop being undefined, then it should say so and simply forbid the prop being undefined. With that, your parent will have to make sure it is defined before passing down to children, which means only the root component is doing the null check and none of the descendants need to anymore.

violet basin
#
export default function App() {
    const [serverConnection, setWs] = useState<WebSocketConnection | null>(null);
    const [isLobby, setIsLobby] = useState<boolean>(true);

    useEffect(() => {
        const webSocketConn = import.meta.env.DEV ? new WebSocketConnection("http://localhost:8080") : new WebSocketConnection("https://liveorlive-server.fly.dev/");
        webSocketConn.subscribeToEvent("onConnect", (_) => { // eslint-disable-line @typescript-eslint/no-unused-vars
            console.log("New connection!");
        });

        setWs(webSocketConn);

        return () => {
            webSocketConn.close();
            setWs(null);
        };
    }, []);

    return isLobby ? (
        <Provider store={store}>
            <Lobby serverConnection={serverConnection} setIsLobby={(value: boolean) => {setIsLobby(value)}} />
        </Provider>
    ) : (
        <Provider store={store}>
            <MainGameUI serverConnection={serverConnection} />
        </Provider>
    )
}

IDK where I'd do the check though

vocal mesa
#

Just do something like:

return serverConnection
    ? /* your existing code */
    : <div>Connecting to server</div>
#

Now your Lobby and MainGameUI component can declare their serverConnection to be non nullable and they don't need to null check anymore.

violet basin
#

Oh, that actually works

#

I'm surprised TS catches that

vocal mesa
#

It does, TS has flow typing which a lot of languages don't have or only have a limited subset of flow typing.

#

Eg C# only has flow typing for nullability but nothing else.

violet basin
#

Oh, cool

#

Alright this is much better

vocal mesa
#

(You can and probably should invert the ternary to a guard clause for better readability)