Rajohan
Rajohan

Reputation: 1415

Specifying types with TypeScript, when <Array> has different kinds of structure

I am kind of new to TypeScript and I am trying to get rid of all any types.

Problem:
Within the React Component, I loop over an array of objects and extract a key/value pair.
The components receives tags, tagKeys props as follows:

tags: [{ name: "Some tag" }] OR [{ platform: { name: "Some tag" } }];

tagKeys: ["name"] OR tagKeys: ["platform", "name"]

If I run this script (below), I get this error in the console:

Element implicitly has an 'any' type because index expression is not of type 'number'

type Props = {
    tags: any[];
    tagKeys: string[];
};

const Tags: React.FC<Props> = ({ tags, tagKeys }: PropsWithChildren<Props>) => {
    const renderTags = (): React.ReactNode => {
        return tags.map(tag => {
            let key = "";
            // Loop through tagKeys in the tags array to extract the tag
            for (let i = 0; i < tagKeys.length; i++) {
                key = i === 0 ? tag[tagKeys[i]] : key[tagKeys[i]];
            }
            return <Tag key={`tag-${key}`} tag={key} />;
        });
    };
    return <StyledTags>{renderTags()}</StyledTags>;
};

If I change the line inside the for loop to...

key = i === 0 ? tag[tagKeys[i] as any] : key[tagKeys[i] as any];

...the script runs but I want to get rid of the any type.

Question:
Is there a way to set the type without specifying how the received props array tags will look?

I want to be able to pass arrays with different kinds of structure and extract a key/value pair.

Upvotes: 1

Views: 382

Answers (2)

MwamiTovi
MwamiTovi

Reputation: 2522

There two issues to watch that we'll need to be clear about.

  • First, you asked:
    Is there a way to set the type without specifying how the received props array tags will look?
type Props = {
    tags: any[]; // This is the answer to your direct question
    tagKeys: string[];
};
  • Secondly, having handled the above, TypeScript is actually complaining about your index.
    As you'll agree, reactjs uses the unique key to render each <Tag>.
// TypeScript is actually warning you about this.

// Note that initially, `key` is of type `string` (implicitly or indirectly )
let key = "";

// Within your loop, you assign `key` to be of type `object`
key = i === 0 ? tag[tagKeys[i]] : key[tagKeys[i]];
// So the above line says that `key` can be `string` or `object`
// Thus TypeScript tells you that...
// implicitly your index (key) expression is of type any

To handle that TypeScript error, update as follows:

let key: any = "";

Kindly make the necessary updates, and let me know how it goes for you.

Upvotes: 3

Aprillion
Aprillion

Reputation: 22340

key can be either a string or an object during it's lifetime, so my first intuition would be to declare it as such:

let key: object | string = "";

But TS is not happy with that, so my second try would be:

type NestedObject = { [key: string]: NestedObject } | string
let key: NestedObject = '';

key = { platform: { name: 'dd' } }
key = key.platform.name

But TS is not happy with the last line either, so at this time I would just give up:

let key: any = '';

Upvotes: 1

Related Questions