skip to Main Content

Do you guys know somehow to indicate type of a variable we know for sure that the value of this variable is in an array in Typescript?

For exemple, I want to display a list of tabs on a tabs bar:

const TabsBar= ({tabs}: {tabs: string[]}) => {
    const [activeTab, setActiveTab] = useState<string>(tabs[0]) //the active tab by default is the first tab in array of tabs

    useEffect(() => {
        setActiveTab(tabs.find(tab => tab === activeTab))//give error Argument of type 'string | undefined' is not assignable                      
//to parameter of type 'string'. How to define type of "tabs" and "activeTab" so that the error disappear?
    }, [tabs, activeTab])
        
    ...
}

And also in the case that a tab has this form {id:number;title:string}, how to make tabs.find(tab => tab.id === activeTab.id) without error?

Thank you!!!

3

Answers


  1. You can use TypeScript generics to define the types of the tabs array and the activeTab state and the as keyword to assert that the result of find won’t be undefined.

    import { useState, useEffect } from 'react';
    
    // Define a generic type for tabs that can be either string or an object with an `id` property
    type Tab = string | { id: number; title: string };
    
    const TabsBar = ({ tabs }: { tabs: Tab[] }) => {
        const [activeTab, setActiveTab] = useState<Tab>(tabs[0]);
    
        useEffect(() => {
            // Check if activeTab is a string or an object with an `id` property
            if (typeof activeTab === 'string') {
                // For string tabs, find the tab by directly comparing values
                setActiveTab(tabs.find(tab => tab === activeTab) as Tab);
            } else {
                // For object tabs, find the tab by matching the `id` property
                setActiveTab(tabs.find(tab => (tab as { id: number }).id === (activeTab as { id: number }).id) as Tab);
            }
        }, [tabs, activeTab]);
    
        // Rest of your component...
    
        return (
            <div>
                {/* Render your tabs here */}
            </div>
        );
    };
    
    export default TabsBar;
    
    Login or Signup to reply.
  2. If you do so :

    const TabsBar= ({tabs}: {tabs: string[]}) => {
      const [activeTab, setActiveTab] = useState<string>(tabs[0]) 
      useEffect(() => {
        const result = tabs.find(tab => tab === activeTab);
        if (result) {setActiveTab(result))};
        }, [tabs, activeTab])
    }
    

    Then, it should be ok, although, it doesn’t exactly reply to your request.

    Otherwise, you would have to put an exclamation mark (non-null assertion operator) here :

        tabs.find(tab => tab === activeTab)!
    
    Login or Signup to reply.
  3. Your specific problems are related to the fact that arrays might be empty, in order to make Typescript like you, do 2 things:

    let’s say Tab is your type, could be string as well.

    1. use this syntax to tell typescript that there’s at least one element in the array.
       const TabsBar = ({ tabs }: { tabs: [Tab, ...Tab[]] }) => {
          // implementation
       }
    

    2.handle the case in which find returns undefined.

       ...
       useEffect(() => {
          const selected = tabs.find(...) //now selected is of type `Tab | undefined`
          if (!selected) throw Error("Unexpected tab") // this shouldn't happen
          // any future usage of `selected` would be Tab without undefined
          setActiveTab(selected)
       }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search