When making a simple rock, paper, scissors game with typescript I cam across an interaction I couldn't understand.
const [gameState, setGameState] = useState<gameState>({
choice: null,
score: 0,
});
function fetchScore() {
let score = localStorage.getItem("score");
if (typeof score === "string") {
setGameState((prev) => ({ ...prev, score:
parseInt(score) })); // Typescript still thinks score can be null
}
}
parseInt throws an error saying score could be set to null despite the type guard.
I did some messing around and found that it was only happening when I used prev state.
const [test,setTest] = useState(0);
function fetchScore() {
let score = localStorage.getItem("score");
if (typeof score === "string") {
setTest(parseInt(score))//Score is correctly guarded
}
}
I am currently handling it by typecasting score as a string but I would like to understand what cause this to happen?
CodePudding user response:
That is because you are using score inside of a callback function. So typescript assumes that score
variable can be changed before callback function will be executed and score potentially can null inside callback function.
Solutions:
the best solution I see is to use const
instead of let
.
const score = localStorage.getItem("score");
this way typescript know that score cannot be changed later.
Also you could check typeof score
inside of callback function and typescript will not complaine. I would prefer const
. But this will work too. Just for understanding how it works here:
function fetchScore() {
let score = localStorage.getItem("score");
setGameState((prev: any) => {
if (typeof score === "string") {
// no complaining, checking inside of callback function
return { ...prev, score: parseInt(score) };
}
return prev;
});
}