#thje GeodeList.map is not a function

209 messages ยท Page 1 of 1 (latest)

torn shore
#
import * as GeodeData from "../../geodes.json" assert { type: "json" }

export const GeodeList = GeodeData as Array<Geode>

export interface Geode {
  name: string
  location: string
  price: GeodePrice
  drops: Array<GeodeDrop>
}

export interface GeodePrice {
  stat: string
  amount: number
}

export interface GeodeDrop {
  stat: string
  odds: number
}

export type GeodeName = typeof GeodeList[number]["name"]

// TypeError: GeodeList.map is not a function. (In 'GeodeList.map((geode) => geode.name)', 'GeodeList.map' is undefined)
export const getGeodeNames = (): Array<GeodeName> => GeodeList.map((geode) => geode.name)
export const getGeodeByName = (name: GeodeName): Geode | undefined => GeodeList.find((geode) => geode.name === name)
lusty stag
#

๐Ÿ‘‰ GeodeList.map is not a function
because GeodeList is undefined
because the import is not right

๐Ÿ‘‰ json imports require a named import
because you are only importing one object
it's not some kind of namespace

#

so import GeodeData from "../../geodes.json" assert { type: "json" };

#

๐Ÿ‘‰ also export type GeodeName = typeof GeodeList[number]["name"] won't give you the proper type you expect

#

since for GeodeList you used a cast

#

so GeodeList: Array<Geode>
so GeodeName: Array<Geode>[number]["name"]
so GeodeName: Geode["name"]
so GeodeName: string
and not literals

#

๐Ÿ‘‰ a few other things:

  • use camelCase for variables names (geodeList)
  • you don't need to repeat the type when it can be inferred
  • you can use [] instead of Array<> (const geodeList: Geode[])
dark latchBOT
#
ascor8522#0

Preview:```ts
import geodeData from "../../geodes.json" assert {type: "json"}

export interface Geode {
name: string
location: string
price: GeodePrice
drops: GeodeDrop[]
}

export interface GeodePrice {
stat: string
amount: number
}

export interface Geode
...```

lusty stag
#

@torn shore

torn shore
torn shore
#

ok

torn shore
torn shore
#

Then what to do if I want a union type of the names of geodes

lusty stag
lusty stag
torn shore
#

Wrong in the way I use it yes that's what I meant by wrong

torn shore
#

I don't see any other big changes than the import

#

Ohhhh

#

I see now

lusty stag
torn shore
#

The variable is gone

#

well not gone but it's not where it was

#

and it's not casted anymore

lusty stag
#

you can directly tap into the import
no need to copy it to some other variable tbh

lusty stag
#

you could also have

export const geodeList = geodeData satisfies Geode[];
export type GeodeName = typeof geodeList[number]["name"];
torn shore
#

I honestly forgot satisfies was even a thing cause I never even knew where and how to use it

lusty stag
#

but tbh geodeList and geodeData are duplicate

torn shore
#

Yeah but you can't export an import can you

lusty stag
#

you could name your import geodeList and use it to get the names directly

lusty stag
#
import geodeList from "../../geodes.json" assert { type: "json" };
export type GeodeName = typeof geodeList[number]["name"];
#

but you don't really have a check using the satisifes operator
you changed the source of truth
from the satisfies Geode[] to the content of the JSON file

torn shore
#

I don't know what satisfies is

#

Okay so

#

How do I make it so that getGeodeByName knows what names are valid

lusty stag
torn shore
#

Um

#

!!

lusty stag
#
import geodeData from "../../geodes.json" assert { type: "json" };
const geodeList: Geode[] = geodeData;
export type GeodeName = typeof geodeData[number]["name"];
```or
```ts
import geodeData from "../../geodes.json" assert { type: "json" };
export const geodeList = geodeData satisfies Geode[];
export type GeodeName = typeof geodeList[number]["name"];
```or
```ts
import geodeList from "../../geodes.json" assert { type: "json" };
export type GeodeName = typeof geodeList[number]["name"];

which are all pretty similar

torn shore
#

I have the first one rn tho

shut badge
#

I think the first one will erase information because it is typed as Geode[], which is a mutable array type

#

The other 2 options might preserve the information. Although I haven't used JSON imports.

lusty stag
shut badge
#

oh

#

You're right

lusty stag
#

1 and 2 provide some additional safety
checking what is imported before using it

while solution 3 doesn't and uses that as source of truth

torn shore
#

2nd also doesn't work

shut badge
#

So does TS type geodeData correctly based on the JSON file?

lusty stag
#

yes

#

it can infer the type from the JSON content

shut badge
lusty stag
torn shore
#

It's JSON

lusty stag
#

might actually be the problem here

torn shore
#

It's an array of geodes like these

shut badge
#

But what type is the variable

#

when you hover it

torn shore
#

ah that

#

I think it just says import <name> waiat

shut badge
#

That's annoying

torn shore
#

It is

shut badge
#

Is the json file an array, with geodes in it?

#

yeah you said it is

#

Does it need to be json?

shut badge
#

It's just { name: string }[]

torn shore
shut badge
#

Will the JSON data change sometimes?

torn shore
#

kinda big

torn shore
shut badge
#

JSON isn't smaller that JS, it's probably larger

shut badge
torn shore
#

No

shut badge
#

OK

#

I think you just need to use a .js file instead

#
export const geodeList = [
  { name: 'foo },
  ...
] as const
#

the as const will give you more info

#

export default instead if you want to

#

JSON is valid JS, so you don't have to change anything with the data

torn shore
#

Yeah I know it is, it just seems odd doing that

shut badge
#

why?

#

Otherwise you need a way to get TS to infer the type of the JSON as const

#
#

You can use a tool to generate d.ts files to describe your json. Or just do it the easy way

torn shore
#

Hey let's go that worked

#

I stored it in a .ts file instead

shut badge
#

yep

#

yeah ts/js whatever

torn shore
#

Yeah

shut badge
#

obviously it does need to be .ts with TypeScript

torn shore
#

long error messages go hard now

#

lol

shut badge
#

Gut

torn shore
#

hol on how do I work this

shut badge
#

you could accept string in getGeodeByName

#

then you have to search geodes to see if there is a match. I don't know how your lookup works

#

maybe you convert the array to an object somewhere

torn shore
shut badge
#

so if you use (name: string) does that work?

#

It must just return undefined or something if it doesn't find it

torn shore
#

Oh right

#

Yeah not today

#

Yeah I don't know what to do

shut badge
#

show me the whole function

#

and error

#

A red line without context isn't helpful

torn shore
#
export const getGeodeByName = (name: string): Geode | undefined => GeodeList.find((geode) => geode.name === name)
Type '{ readonly name: "Mint"; readonly location: "Minty Grooves"; readonly price: { readonly stat: "Mint"; readonly amount: 2000; }; readonly drops: readonly [{ readonly stat: "Mint"; readonly odds: 2; }, { readonly stat: "Alpha Point"; readonly odds: 2; }, ... 5 more ..., { ...; }]; } | ... 33 more ... | undefined' is not assignable to type 'Geode | undefined'.
  Type '{ readonly name: "Mint"; readonly location: "Minty Grooves"; readonly price: { readonly stat: "Mint"; readonly amount: 2000; }; readonly drops: readonly [{ readonly stat: "Mint"; readonly odds: 2; }, { readonly stat: "Alpha Point"; readonly odds: 2; }, ... 5 more ..., { ...; }]; }' is not assignable to type 'Geode'.
    Types of property 'drops' are incompatible.
      The type 'readonly [{ readonly stat: "Mint"; readonly odds: 2; }, { readonly stat: "Alpha Point"; readonly odds: 2; }, { readonly stat: "Chestnut"; readonly odds: 5; }, { readonly stat: "Bat"; readonly odds: 8; }, { ...; }, { ...; }, { ...; }, { ...; }]' is 'readonly' and cannot be assigned to the mutable type 'GeodeDrop[]'
shut badge
#

seems to be a problem with your return type annotation

torn shore
#

I guess

#

But like

#

that's what it returns

shut badge
#

doesn't sem to be

torn shore
#

I mean what else can ```ts
GeodeList.find((geode) => geode.name === name)

shut badge
#

Maybe GeodeDrop[] should be readonly GeodeDrop[]

#

I don't know what the Geode type is

#

But, you can just remove the return type annotation, right?

torn shore
#
import GeodeList from "../GeodeList"

export interface Geode {
  name: string
  location: string
  price: GeodePrice
  drops: Array<GeodeDrop>
}

export interface GeodePrice {
  stat: string
  amount: number
}

export interface GeodeDrop {
  stat: string
  odds: number
}

export type GeodeName = typeof GeodeList[number]["name"]

export const getGeodeNames = () => GeodeList.map((geode) => geode.name)
export const getGeodeByName = (name: string): Geode | undefined => GeodeList.find((geode) => geode.name === name)
shut badge
#

and TS will infer the return type

torn shore
shut badge
#

really?

torn shore
#
const geodeName = commandMessage.args[0]
const longNumbers = commandMessage.hasFlag("long-numbers")
const targetGeode = Geodes.getGeodeByName(geodeName)

if (!targetGeode) {
  await commandMessage.reply(BotMessage.create(Prefix.ERROR, INVALID_GEODE_NAME))
  return
}

await commandMessage.replyWithEmbed(this.getGeodeEmbed(targetGeode, longNumbers))

see this is one of the uses

shut badge
#
export interface Geode {
  name: string
  location: string
  price: GeodePrice
  drops: Array<GeodeDrop>
}

to

export interface Geode {
  name: string
  location: string
  price: GeodePrice
  drops: readonly Array<GeodeDrop>
}
torn shore
#

Yeah I tried that

#

it errors

#

one sec

shut badge
#

Well you're gonna have to go through and fix errors until it works ๐Ÿ™‚

torn shore
#
'readonly' type modifier is only permitted on array and tuple literal types.ts(1354)
shut badge
#

Oh

torn shore
#

Yeah

shut badge
#

ReadonlyArray<GeodeDrop> ?

torn shore
#

Ooooh right

shut badge
#

Because you are using the non standard Array helper

#

instead of just normal []

torn shore
#

Niceee that works

torn shore
shut badge
#

readonly modifys arrays, but Array is a generic type that returns an array

#

So, yeah

torn shore
#

dude

shut badge
#

duuuude

#

Yeah

#

because you accept string

torn shore
#

right

shut badge
#

but I think there is a trick

#

lemme see if I can find it

torn shore
#

Thank you autocomplete, that's exactly what I wanted here

shut badge
#

(name: string & GeodeName)

torn shore
#

I just kinda casted it but I don't think that's the way to go...?

shut badge
#

what happens now?

torn shore
#

Oh I tried a union but not an intersection interesting

shut badge
#

or (name: string & GeodeName & {}

#

I'm sure there is a trick to making it autocomplete

torn shore
#

it just ignores & string seemingly lol

#

isn't {} like any

shut badge
#

right

#

GeodeName | (string & {})

shut badge
#

yeah & would require matching both, so it would erase string

torn shore
#

Oh, that works!

shut badge
#

Yeah thanks to Mr Pocock

torn shore
#

Oooooohh!!

shut badge
#

any whoever discovered the trick

torn shore
#

I'm subbed to him

#

I remember his video about {} now

#

Nice dude, thanks

shut badge
#

Another option is to check whether the input in a correct name before finding. This lets you make a find function that always returns a match, never undefined, which is quite nice

#

You would use a type guard function there probably

#

anyhow, I'm off

torn shore
torn shore
shut badge
#

Bye

shut badge
torn shore
#

oh.

shut badge
#

But you need to generate an array of geodeNames first

torn shore
#

yeah that's the bad thing

#

that went bad

#

that was not good

shut badge
#

I don't think you wannt to manually make a list here, too easy to make a mistake

torn shore
#

yes

#

exactly

shut badge
#

but you can generate a list from your data. You can also generate an object from your data.

#

Then you can have

const geodeObject = {
  "diamond": { ... }
}
#

then you don't need your findGeodeByName thing, because you can just write geodeObject.diamond

torn shore
#

huh

#

ah

shut badge
#

You just loop through your geodesList and make an object

#

But you probably need a mapped type to avoid losing info, which is a bit complicated to write at your level

torn shore
#

aaa

#

gotta hate when languages don't magically start working the way you want them to

shut badge
#

If you write your data (that was JSON) as an object to begin with, that's easier...

#

not sure if it needs to be an array

torn shore
#

Why not

#

I mean the way I use it, apart from this, it would be better off as an array I think

#

Okay thanks you both, I'm good with what I have rn

lavish sequoia
shut badge
#

Is there another way to import json?