#Creating a type from filtered object

46 messages · Page 1 of 1 (latest)

rain kilnBOT
#
strobe#9977

Preview:ts type OptionData = { name: string; value: number; available: boolean } const options = { AAA: { name: 'hello_a', value: 1234, available: true }, BBB: { name: 'apple_b', value: 1212, available: false }, CCC: { name: 'spaghetti_c', value: 531, available: true } ...

low crescent
#

question is in the code snippet

#

note: i'm not fixated on the actual data structure being Record<string, OptionData> so if that needs to be refactored to get it to work then that's fine

stark veldt
#
type Options = typeof options
type AvailableOptions = {
    [K in keyof Options]: Options[K]['available'] extends true ? K : never
}[keyof Options]
low crescent
#

the fact AvailableOptions type isn't paired to the availableOptions const is a bit weird to me, but i guess it makes sense

low crescent
stark veldt
#

You'd need to preserve that information, then sure you can do that.

low crescent
#

can the Object. utilities work with generics like that? i'm thinking it's more of a runtime issue?

stark veldt
#

You'd need to cast because of the whole excess properties thing.

#

!:unsafe-keys

rain kilnBOT
#
retsam19#0
`!retsam19:unsafe-keys`:

Since TS allows objects to have extra properties not specified in the type, it doesn't assume that all the keys on the type are the only keys on the object. This means that Object.keys returns string[] not a specific type, and for(const key in obj), key is string, (not keyof typeof obj).

If you wish to assume otherwise, this utility is often helpful:

// A signature for `Object.keys` that assumes the only keys are the ones indicated by the type
const unsafeKeys = Object.keys as <T>(obj: T) => Array<keyof T>;
low crescent
#

interesting

rain kilnBOT
#
strobe#9977

Preview:ts type OptionData = { name: string; value: number; available: boolean } const options = { AAA: { name: 'hello_a', value: 1234, available: true }, BBB: { name: 'apple_b', value: 1212, available: false }, CCC: { name: 'spaghetti_c', value: 531, available: true } ...

low crescent
#

how do you go about filtering out the values properly?

stark veldt
#

Not exactly sure what you are going for, are you looking for something like this?

const finalOptions: FinalData[] = Object.values(options).map(v => ({ name: v.name, value: v.value }))
low crescent
#

basically just trying to drop 'available'

#

so your snippet wouldn't work as i miss the key

#

maybe the starting data type is wrong for this approach. i could change it to

{ key: 'AAA', name: 'hello_a', value: 1234, available: true }

my first idea was to treat it a bit like an enum

#

oh wait i see where it's going wrong

stark veldt
#

The original starting data type can work, I'm just not sure what final shape you are looking for.

#

Because FinalData is just one option but you are looking for multiple options in finalOptions?

#

And FinalData also doesn't have the key anywhere.

low crescent
#

recreate options, but without 'available' attribute

#

the plan was to feed availableOptions into it

stark veldt
#

Then it's just Object.fromEntries(availableOptions), right?

low crescent
#

i think i've confused myself by bouncing around data types

#
type OptionData = { name: string; value: number; available: boolean }
const options = {
    AAA: { name: 'hello_a', value: 1234, available: true },
    BBB: { name: 'apple_b', value: 1212, available: false },
    CCC: { name: 'spaghetti_c', value: 531, available: true }
} as const satisfies Record<string, OptionData>

const availableOptions = Object.entries(options).filter(([k, v]) => v.available)

given this, i want availableOptions to be ['AAA', 'CCC']

#

although,

{
  AAA: { name: 'hello_a', value: 1234, available: true },
  CCC: { name: 'spaghetti_c', value: 531, available: true }
}

this could also work, i'm a bit unsure which is more efficient

#

decision: ['AAA', 'CCC']

#

@stark veldt this is what i was after. does it look efficient or wasteful?

#

oh wait, moment

low crescent
#

can this be improved?

#

i'm wondering also if it'd just be better to declare things statically rather than evaluate them at runtime

rain kilnBOT
#
strobe#9977

Preview:ts type OptionData = { name: string; value: number; available: boolean } const options = { AAA: { name: 'hello_a', value: 1234, available: true }, BBB: { name: 'apple_b', value: 1212, available: false }, CCC: { name: 'spaghetti_c', value: 531, available: true } ...

stark veldt
#

Looks fine to me, I mean it's not like you will be running this in a hot path so it's just an one time cost.

#

Idk if that's really worth it though.

rain kilnBOT
#
strobe#9977

Preview:```ts
type OptionData = { name: string; value: number }
const options = {
AAA: { name: 'hello_a', value: 1234 },
BBB: { name: 'apple_b', value: 1212 },
CCC: { name: 'spaghetti_c', value: 531 }
} as const satisfies Record<string, OptionData>

type Options = typeof options
...```

low crescent
#

if you do something like this there's less runtime evaluation, but it's requiring a bit more static input

#

it can probably be written better too, that's a prototype

#
type Options = typeof options

const availableOptions = ['AAA', 'CCC'] as const satisfies Array<keyof Options>
type AvailableOptions = typeof availableOptions[number]

that's probably better

rain kilnBOT
#
nonspicyburrito#0

Preview:```ts
type OptionData = { name: string; value: number }

const options = {
AAA: { name: 'hello_a', value: 1234 },
BBB: { name: 'apple_b', value: 1212 },
CCC: { name: 'spaghetti_c', value: 531 },
} as const satisfies Record<string, OptionData>
type Options = typeof options
...```

stark veldt
#

Yeah we had the same idea.

low crescent
#

considering i have control over the options data and it's not from an external source, it seems a bit redundant to add the additional js manipulation just to keep it as a single configuration

#

i'm tempted to use this last example tbh

#

leans more into TS than JS