rustyBucketBay
rustyBucketBay

Reputation: 4561

Get the type of a map returned object. Typescript ts(2345)

I have a code working with the any type, check the comment //HERE TYPE ANY: I need that to be able to push data into that array, as you will be able to se below in the code in the //NEED ANY TYPE TO PUSH INTO THE ARRAY line.

const ProjectList = () => {
  const [projects, setProjects] = useState<IFirebaseProject[]>();
  useEffect(() => {
    let data:any[] = []; //HERE TYPE ANY
    firebase
      .firestore()
      .collection("projects")
      .onSnapshot((snapshot) => {
        let changes = snapshot.docChanges();
        changes.forEach(change => {console.log(change.doc.data())})
        const fbProjects = changes.map( (change) => ({
          id: change.doc.id,
          ...change.doc.data()
        }));
        fbProjects.forEach(fbProject => data.push(fbProject)) //NEED ANY TYPE TO PUSH INTO THE ARRAY
        setProjects([...data]);
      });
  }, []);

My problem is that I cannot enforce the type (although I know it) for the specific part of the code:

const fbProjects = changes.map( (change) => ({
    id: change.doc.id,
    ...change.doc.data() 
}));

I know the type obtained by ...change.doc.data(), but how can I enforce my type, so that fbProjects meets a determined type which I define?

This is the type of fbProjects:

interface IFirebaseProject {
  id: string,
  authorFirstName: string,
  authorId: string,
  authorLastName: string
  content: string
  createdAt: Date
  title: string
}

So it has the id property, and I know that ...change.doc.data() has the rest of the properties.

If I use my IFirebaseProject instead of any I obtain an error when I push the project in the array (fbProjects.forEach( fbProject => data.push(fbProject))) because obviously does not meet the type.

The error I obtain is: Argument of type '{ id: string; }' is not assignable to parameter of type 'IFirebaseProject'. Type '{ id: string; }' is missing the following properties from type 'IFirebaseProject': authorFirstName, authorId, authorLastName, content, and 2 more.ts(2345)

Edit: Console output:

enter image description here

(One of this objects for each of the registers in the database logged with the .forEach)

Upvotes: 0

Views: 549

Answers (2)

Steve Holgado
Steve Holgado

Reputation: 12071

You could tell TypeScript to treat the object returned from map as IFirebaseProject:

const fbProjects = changes.map((change) => ({
  id: change.doc.id,
  ...change.doc.data()
}) as IFirebaseProject);

Upvotes: 1

rustyBucketBay
rustyBucketBay

Reputation: 4561

I split my types like this:

interface IFirebaseProject extends Destructured {
  id: string,
  
}

interface Destructured {
  authorFirstName: string,
  authorId: string,
  authorLastName: string
  content: string
  createdAt: Date
  title: string
}

Then I could set the type of the destructured part like this.

const fbProjects = changes.map( (change) => ({
    id: change.doc.id,
    ...change.doc.data() as Destructured
}));

That did the work. In case there is a more proficient way to solve this, I'll be glad to know.

Upvotes: 1

Related Questions