RYOTA
RYOTA

Reputation: 53

How do I fix the error "Element implicitly has an 'any' type because the expression of type

I am a beginner in TypeScript.

I used ts migrate to replace the JavaScript with TypeScript in my react app, but I got the following error.

Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{}'. TS7053

The files that caused the error are the following.

import React, { useState, useEffect, useCallback} from 'react';
import './assets/styles/style.css';
import {AnswersList, Chats} from './components/index';
import FormDialog from './components/Forms/FormDialog';
import {db} from './firebase/index';

const App = () => {
  const [answers, setAnswers] = useState([]);
  const [chats, setChats] = useState([]);
  const [currentId, setCurrentId] = useState("init");
  const [dataset, setDataset] = useState({});
  const [open, setOpen] = useState(false);

  const displayNextQuestion = (nextQuestionId: any, nextDataset: any) => {
    addChats({
      text: nextDataset.question,
      type: 'question'
    })

      setAnswers(nextDataset.answers)
      setCurrentId(nextQuestionId)
  }

  const selectAnswer = (selectedAnswer: any, nextQuestionId: any) => {
    switch(true) {
      case (nextQuestionId === 'contact'):
          handleClickOpen()
          break;
      
      case (/^https:*/.test(nextQuestionId)):
          const a = document.createElement('a');
          a.href = nextQuestionId;
          a.target = '_blank';
          a.click();
          break;

      default:
          addChats({
              text: selectedAnswer,
              type: 'answer'
          })
        setTimeout(() => displayNextQuestion(nextQuestionId, dataset[nextQuestionId]), 500) //Here's where the error occurs
        break;
    }
  }

  const addChats = (chat: any) => {
    setChats(prevChats => {
      return [...prevChats, chat]
    })
  }

  const handleClickOpen = () => {
    setOpen(true)
  };

  const handleClose = useCallback(() => {
      setOpen(false)
  }, [setOpen]);


  useEffect(() => { 
    (async() => {
      const initDataset = {};

      await db.collection('questions').get().then(snapshots => {
        snapshots.forEach(doc => {
          const id = doc.id
          const data = doc.data()
          initDataset[id] = data
        })
      })

      setDataset(initDataset)
      displayNextQuestion(currentId, initDataset[currentId])
    })()
  }, [])

  useEffect(() => {
      const scrollArea = document.getElementById('scroll-area')
      if (scrollArea) {
        scrollArea.scrollTop = scrollArea.scrollHeight
      } 
  })
  
  return(
    <section className="c-section"> 
      <div className="c-box">
        <Chats chats={chats} />
        <AnswersList answers={answers} select={selectAnswer} />
        <FormDialog open={open} handleClose={handleClose} />
      </div>
    </section>
  );
}

export default App;

First, I defined the type of displayNextQuestion using the interface as follows, but the error was not resolved and we got another error.

  interface StringKeyObject {
    [key: string]: any;
  }  

  const displayNextQuestion: StringKeyObject = (nextQuestionId: any, nextDataset: any) => {
    addChats({
      text: nextDataset.question,
      type: 'question'
    })

      setAnswers(nextDataset.answers)
      setCurrentId(nextQuestionId)
  }

This expression is not callable. Type 'StringKeyObject' has no call signatures. TS2349

Next, the displayNextQuestion's argument nextQuestionId was of type any, so I set it to string, but the error did not change.

I did a lot of research and did not know how to solve this error, so I asked.

Upvotes: 1

Views: 2010

Answers (2)

Sam R.
Sam R.

Reputation: 16450

When you say:

const displayNextQuestion: StringKeyObject = (...)

This means displayNextQuestion is of type StringKeyObject which is clearly not. It's a function. You are not returning anything from this function either so the return type is void as well.

Now let's see the parameters. nextQuestionId must be a string since you use setCurrentId(nextQuestionId) and its default state is init.

nextDataset should be an object that has array of answers like:

{ 
  answers: [...],
  ...
}

Putting it all together something like this should work:

const displayNextQuestion = (nextQuestionId: string, nextDataset: { answers: Array<Answer> } ) => {
  addChats({
   text: nextDataset.question,
   type: 'question'
  })

  setAnswers(nextDataset.answers)
  setCurrentId(nextQuestionId)
}

I suggest you define an interface for the dataset as well as answer:

interface Answer {
  // ...whatever that answer has
}

interface Dataset {
  answers: Array<Answer>;
  // ...other things that Dataset has
}

And explicitly set the type for useState:

interface Question {}

interface Chat {}

interface Answer { }

interface Dataset {
  answers: Array<Answer>;
  question: any;
  [k: string]: Question; 
  // ^ This signifies that Dataset can have any key as long as it's string 
  // and its value is of type Question (best I could guess based on the 
  // usage at "dataset[nextQuestionId])"
}

const App = () => {
  const [answers, setAnswers] = useState<Array<Answer>>([]);
  const [chats, setChats] = useState<Array<Chat>>([]);
  const [currentId, setCurrentId] = useState("init");
  const [dataset, setDataset] = useState<Dataset>({ answers: [], question: null });
  const [open, setOpen] = useState(false);
  // ...

Upvotes: 2

Won Gyo Seo
Won Gyo Seo

Reputation: 442

I'm not sure, because there is no mention of exactly where is the line the error occurs.

Maybe I think u should use Generics for useState.

could you check below is worked?

type DataSet = {
 // write the form of state to be managed.
}

const [dataset, setDataset] = useState<DataSet>({});

Upvotes: 0

Related Questions