1

So I'm building a quiz app and that quiz is divided into difficulty levels. I change that level on a button click by setting params into my path. Then I try to insert a difficulty index because the structure of my obj is:

const questions = {
  easy: [...],
  medium: [...],
  hard: [...]
}

  const [searchParams, setSearchParams] = useSearchParams();
  const [currentPage, setCurrentPage] = useState(0);

  const difficulty = searchParams.get("difficulty");

  const handlePageChange = () => {
    if (currentPage + 1 < questions[difficulty].length) {
      setCurrentPage((prevState) => prevState + 1);
    }
  };

  const handleDifficultyChoice = (difficulty: DifficultyType) => {
    setSearchParams({ difficulty });
  };

Unfortunately I cannot insert that index because index cannot be null. How to solve that?

Adrian
  • 173
  • 9
  • 2
    [Please do not upload images of code/data/errors.](//meta.stackoverflow.com/q/285551) – Heretic Monkey Dec 01 '22 at 18:38
  • 1
    Does this answer your question? [Type 'null' cannot be used as an index type](https://stackoverflow.com/questions/46043087/type-null-cannot-be-used-as-an-index-type) – Heretic Monkey Dec 01 '22 at 18:40
  • so `const difficulty = searchParams.get("difficulty");` is null. The variable `difficulty ` is not going to update when you set The search params. – epascarello Dec 01 '22 at 18:44
  • 1
    Also, we should know the type of `questions`. What you paste is not useful, the TS type is. – tokland Dec 01 '22 at 18:44

2 Answers2

1

You can replace the contents of the handlePageChange function with the following code, which will guarantee that difficulty is a valid property key in questions before executing the code after the conditional expression:

if (
  // This expresion evaluates to true or false,
  // depending on whether difficulty is a valid property key in questions:
  (difficulty as DifficultyType) in questions
  // If it evaluates to false, this one won't execute:
  && currentPage + 1 < questions[difficulty as DifficultyType].length
  // and neither will the setCurrentPage invocation:
) setCurrentPage((prevState) => prevState + 1);

Reference: The difficulty as DifficultyType syntax is what's called a type assertion.

Here's a full, reproducible example in the TypeScript Playground:

import {useState} from 'react';
import {useSearchParams} from 'react-router-dom';

const questions = {
  easy: [],
  medium: [],
  hard: []
};

type DifficultyType = keyof typeof questions;

function Component () {
  const [searchParams, setSearchParams] = useSearchParams();
  const [currentPage, setCurrentPage] = useState(0);

  const difficulty = searchParams.get("difficulty");

  const handlePageChange = () => {
    if (
      (difficulty as DifficultyType) in questions
      && currentPage + 1 < questions[difficulty as DifficultyType].length
    ) setCurrentPage((prevState) => prevState + 1);
  };

  const handleDifficultyChoice = (difficulty: DifficultyType) => {
    setSearchParams({ difficulty });
  };
}


See also: type guard functions in the TS handbook

jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • Ok, i havent seen ```keyof typeof``` before. So basically if i had a hardcoded type for ```questions``` object it would be just e.g. ```keyof QuestionsType```. Since i dont have that hardcoded type and the type is only inferred by TS i can use ```keyof typeof``` to additionaly detect a type, right? – Adrian Dec 01 '22 at 19:13
  • 1
    [^](https://stackoverflow.com/questions/74646642/how-to-fix-type-null-cannot-be-used-as-an-index-type/74646860?noredirect=1#comment131758849_74646860) @Adrian That's right — but you should always make sure that the inferred type is actually what you want in those cases (hover it and use the IntelliSense quick info box to see what TS thinks the type is): object literal property values are not inferred as literals unless you use a [`const` assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions). – jsejcksn Dec 01 '22 at 19:20
0

URLSearchParams.get returns a string value or null.

Check if difficulty is truthy prior to using it as a dynamic key.

Example:

const handlePageChange = () => {
  if (difficulty && currentPage + 1 < questions[difficulty]?.length) {
    setCurrentPage((prevState) => prevState + 1);
  }
};
Drew Reese
  • 165,259
  • 14
  • 153
  • 181