Why am I getting "Type 'String[] | undefined' is not an array type." for my object variable?
Asked Answered
H

2

7

I am trying to create a state variable named hidden to be a dictionary (ex: [{'cutomkey1':'somevalue', 'customkey2':'somevalue'}]). hidden can be empty [{}].

In one of my methods I want to push an item {'asd':'asd'} to the hidden state variable and add them up.

I keep getiting this error:

Type 'String[] | undefined' is not an array type.

I'm new to typescript and I am not sure i set all the variables correctly. this is my code (only the relevant parts):

import React from 'react';

export type AppState = {
    tickets?: Ticket[],
    hidden?: Array<String>,
    search: string;
}

export class App extends React.PureComponent<{}, AppState> {

    state: AppState = {
        search: '',
        hidden: [] 
    }

    hideCard = (e: React.SyntheticEvent<EventTarget>) => {
        let targ = e.target as HTMLElement
        let parent = targ.parentElement as HTMLElement  
        let dict: Map<string, string> = new Map();
        dict.set(parent.id, parent.id)
        this.setState({
            hidden: [...this.state.hidden, {dict}]
        })
        console.log(this.state.hidden)
        parent.classList.add('hideTkt')
    }



}

export default App;

new code:

export type AppState = {
    tickets?: Ticket[],
    hidden?: Array<String> | undefined,
    search: string;
}

export class App extends React.PureComponent<{}, AppState> {

    state: AppState = {
        search: '',
        hidden: [] 
    }


    hideCard = (e: React.SyntheticEvent<EventTarget>) => {
        let targ = e.target as HTMLElement
        let parent = targ.parentElement as HTMLElement  
        let dict: Map<string, string> = new Map();
        dict.set(parent.id, parent.id)
        if (this.state.hidden) {
            this.setState({
                hidden: [...this.state.hidden, {dict}]
            })
        }
        console.log(this.state.hidden)
        parent.classList.add('hideTkt')
    }

this is the error:

Type '(String | { dict: Map<string, string>; })[]' is not assignable to type 'String[]'. Type 'String | { dict: Map<string, string>; }' is not assignable to type 'String'. Type '{ dict: Map<string, string>; }' is not assignable to type 'String'. Object literal may only specify known properties, and 'dict' does not exist in type 'String'.

Hew answered 3/3, 2021 at 13:9 Comment(3)
Remove the optional modifier hidden: Array<String>,Jd
@ritaj removing it results Type '(String | { dict: Map<string, string>; })[]' is not assignable to type 'String[]'. Type 'String | { dict: Map<string, string>; }' is not assignable to type 'String'. Type '{ dict: Map<string, string>; }' is not assignable to type 'String'. Object literal may only specify known properties, and 'dict' does not exist in type 'String'. and as said, hidden can be empty, shouldn't this be there ?Hew
hidden: Array<Record<string, string>>Jd
G
12

The declaration hidden?: Array<String>, means that the property could either be a Array<String> OR undefined.

Using the property like this will fail:

hidden.push(item)

because hidden could be undefined (and undefined has no property push).

If you check if this.state.hidden is available, typescript won't complain anymore:

if(this.state.hidden)
  this.setState({
              hidden: [...this.state.hidden, {dict}]
          })

or you can provide a default value using nullish concealing:

...(this.state.hidden ?? [])
Germicide answered 3/3, 2021 at 13:21 Comment(4)
haha i actualy did try using push yesterday :) I am getting a new error with this change, i am editing my main post with the code.Hew
@W.Doch Usually you should post a new question when you're getting a different error, since having multiple questions/problems in one question is awkward to answer. Type '(String | { dict: Map<string, string>; })[]' is not assignable to type 'String[]' means you're trying to push a wrong type to the array, in this case dict.Germicide
@W.Doch You could declare hidden as a union type of String and { dict: Map<string, string>; } as a start. I would look into having only one type per array though, since untion type arrays are difficult to handle.Germicide
It seems to me the accepted answer is not valid. To be short, it gives a solution to one problem and writes some piece of code which will create another (tougher) problem ...this is the problem my answer is decomposing to make the TS Error understandable ,and solve the tricky problem ;)Selfabuse
S
3

If you read attentively the code that creates your 'hidden' array ,

     if (this.state.hidden) {
            this.setState({
                hidden: [...this.state.hidden, {dict}]
            })
        }

you can see that you spread the state.hidden elements (no pb with those , they already are of the correct type, any type, we don't matter ,as they are coming from a previous well typed and built array ),but, then you add a litteral object who has a property called dict , of type Map<string, string>.

This means your array will contain objects that have a "dict" property respecting this interface :{dict:Map<string,string>} . This is why you have this strange error message about this object litteral with a dict property which does not exist on type string (obviously, the items in your array are not strings)

What you can do is :

  1. type hidden as an Array<{dict:Map<string,string>}>.
  2. spread the content of Map<string,string> inside your array when building it (hidden : [...this.state.hidden,...dict]) , which will leads you to have an Array<Record<string,string>> (Record<K,V> being the type of each key value pair item you have in your Map)
  3. create an interface like :
    interface MyInterface
     { 
      dict:Map<string,string>;
    }

then type your array as Array <MyInterface>

  1. Use the Array.from(dict.entries()) méthod to get an array of tuples ([key:string,value:string]), then use the array map operator to transform your entries into something that feets your needs better in this situation (maybe the string you wanted in the first place) by concatening the key and the value of each tuple item (or anything else that gives you a unique string). In another way, if you just keep the array that you get from "Array.from" , you will have the kind of structure you want (an array of keyValue Pair tuples)

There are lots of other options , but those are the main tracks you can follow . And , at the end , if you really want your "hidden" state variable to be a dictionary ,type it as a Map<string,string> from the beginning , and when you reaffect-it , just do

     if (this.state.hidden) {
            this.setState({
                hidden: new Map([...Array.from(this.state.hidden.entries()), ...Array.from(dict.entries())])
            })
        }

You don't have to do all this gym if you target ES6 , as this will work :

     if (this.state.hidden) {
            this.setState({
                hidden: new Map([...this.state.hidden, ...dict])
            })
        }

And , BTW , I think when you set the objects in your Map , you want to do dict.set(target.id, parent.id) instead of dict.set(parent.id, parent.id) .If not , what's the point of using a Map here ?

Selfabuse answered 3/3, 2021 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.