#Condition is always true since types object and undefined have no overlap

67 messages · Page 1 of 1 (latest)

rose glen
#

I have an array of objects like:

const menuItems = ref([
    {
        id: 'home',
        name: 'Home',
        routerPath: '/',
        submenus: null
    },
    {
        id: 'about',
        name: 'About',
        routerPath: '/about',
        submenus: [
            {
                id: 'behfar-negin',
                name: 'Behfar Negin',
                routerPath: '/about/behfar-negin'
            }
        ]
    },
    
    {
        id: 'blog',
        name: 'Blog',
        routerPath: '/blog',
        submenus: null
    }
]);

And I have defined a function where I change the active menu based on the name it's given:

function setActiveMenu(name: string) {
    let target = menuItems.value.find(item => item.name.toLowerCase() == name.toLowerCase());
    if(target != undefined)
        target.name = name;
} 

However I get this on the != comparison:

Condition is always true since types '{name: UnwrapRef<string>, id: UnwrapRef<string>, routerPath: UnwrapRef<string>, submenus: UnwrapRef<null>} | {name: UnwrapRef<string>, id: UnwrapRef<string>, routerPath: UnwrapRef<string>, submenus: UnwrapRef<{name: string, id: string, routerPath: string}[]>} | {name: UnwrapRef<string>, id: UnwrapRef<string>, routerPath: UnwrapRef<string>, submenus: UnwrapRef<({name: string, id: string} | {name: string, id: string} | {name: string, id: string} | {name: string, id: string})[]>} | {name: UnwrapRef<string>, id: UnwrapRef<string>, routerPath: UnwrapRef<string>, submenus: UnwrapRef<null>}' and 'undefined' have no overlap 

However I also see this in MDN.
Do I have to do something else?

The find() method of Array instances returns the first element in the provided array that satisfies the provided testing function.
If no values satisfy the testing function, undefined is returned.

#

And my guess is that doing target.name = name won't change the array, so do I have to do it by index?

#

or like is there an alternative way

safe geode
#

I would use an id for a menu/dropdown and use that to index into something like an object to get the associated data. in plain html/js something like

<select id="menu">
  <option value="home">Home</option>
  <option value="about">About</option>
  <option value="blog">Blog</option>
</select>
const menuItemsById = {
  home: { name: "Home", routerPath: "/" },
  about: { name: "About", routerPath: "/about" },
  blog: { name: "Blog", routerPath: "/blog" },
}
const menuItemId = document.getElementById("menu").selectedOptions[0].value
const menuItem = menuItemsById[menuItemId]
#

with vue you can probably generate that html options list from the menuItems data itself, which might be what you're already doing anyway

rose glen
#

but why is it even giving me this warning if MDN docs explicitly say if not found it returns undefined

safe geode
#

using find is just going to slow things down since it has to search through your entire array every time to find the element it needs

safe geode
#

cause this doesnt produce any errors for me:

smoky shadowBOT
#
littlelily#0

Preview:```ts
import {ref} from "vue"

const menuItems = ref([
{
id: "home",
name: "Home",
routerPath: "/",
submenus: null,
},
{
id: "about",
name: "A
...```

rose glen
safe geode
#

is there a reason you're not using that :key='menuItem.id' when looking up the menu item and are instead looking it up by menuItem.name which might not even be unique down the line?

rose glen
#

wait yeah I made that mistake

safe geode
#

exactly, so why are you using it inside your setActiveMenu function

rose glen
#
function setActiveMenu(id: string) {
    if(id.toLowerCase() == 'none')
        activeMenu.value = 'None';
    let result = menuItems.value.find(menu => menu.id.toLowerCase() == id.toLowerCase());
    if(result != undefined)
        activeMenu.value = result.id;
}
safe geode
#

in general, avoid find for things that will be run repeatedly. if it's just a single time you need to do it on a smallish array then it's fine, but dont rely on it for continuous lookups, that's what objects and Maps are for

rose glen
#

so should I do it like a map of menus instead of an array of menus?

safe geode
#

yeah, if you need you can transform your array into a map/object, so you can have both structures

rose glen
#

it's gonna be a small array tho, maybe 5-10 elements tops

safe geode
#

still, doing something like const menuItemsById = Object.fromEntries(menuItems.map(m => [m.id, m])) isnt difficult

#

the version for Map would be const menuItemsMap = new Map(menuItems.map(m => [m.id, m]))

#

you might need a .value in there to unwrap the ref into the raw array

rose glen
#

So keep the menu items a list but whenever doing a lookup construct a map from it?

safe geode
#

yeah

#

well dont construct the map every time, store it somewhere

rose glen
#

hmm, I could use a reactive object but that's for objects

#

not arrays

safe geode
#

they both have O(1) lookups by key

rose glen
# smoky shadow
Errors in code
Cannot find module 'vue' or its corresponding type declarations.
Parameter 'item' implicitly has an 'any' type.
'setActiveMenu' is declared but its value is never read.

could also be because of this

safe geode
#

where are you getting that?

rose glen
#

the playgrould link you sent

safe geode
#

in your code or the playground?

rose glen
#

errors section

safe geode
#

you just have to wait for it to load

#

theres no errors

rose glen
#

hmm weird, I've been waiting for a while

safe geode
#

sometimes it bugs out when loading libraries

#

maybe give it a refresh

rose glen
#

sure

#

I also copied your code in vue playground and it doesn't show a warning on the != either

safe geode
#

what's your tsconfig look like?

rose glen
#
{
  "files": [],
  "references": [
    {
      "path": "./tsconfig.node.json"
    },
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.vitest.json"
    }
  ],
  "compilerOptions": {
    "target": "ES2017",
    "module": "ESNext",
    "alwaysStrict": true,
    "noEmitOnError": true,
    "allowJs": false,
    "allowImportingTsExtensions": true,
    "importHelpers": true,

  }
}
safe geode
#

even with those settings TS playground doesnt error.

#

what TS version?

rose glen
#

wait wtf
it says 4.5.5 💀

safe geode
#

that's modern enough

#

that still isnt it

#

maybe you have a linter and that's not actually a TS compile error?

#

like is eslint installed on the project?

rose glen
#

yeah

safe geode
#

see if putting // eslint-disable-next-line before it helps then

rose glen
#

Nope

#

But there's also Volar so maybe Volar is showing this?

rose glen
#

Trying to replicate it on playground

rose glen
#

I even tried !==

#

but no hope

smoky shadowBOT
#
arshiaaghaei#0

Preview:```ts
import {ref} from "vue"

const activeMenu = ref("None")

function setActiveMenu(id: string) {
if (id.toLowerCase() === "none") {
activeMenu.value = "None"
return
}
let result = menuItems.value.find(
menu =>
menu.id.toLowerCase() === id.toLowerCase()
)
i
...```

rose glen
#

interesting

rose glen
#

I did

const mapOfMenus = new Map(menuItems.value.map(menu => [menu.id, menu]));

and

let result = mapOfMenus.get(id);
if(result !== undefined)
    activeMenu.value = id;
#

But I still get that warning

rose glen
#
if(mapOfMenus.has(id)) {
    let result = mapOfMenus.get(id);
    if(result.submenus.length != 0)
        return result.submenus;
}
#

So I did this instead

#

And I get this warning now

#

Vue:  result  is possibly  undefined