#Exclude key from object and map object values

1 messages · Page 1 of 1 (latest)

willow moss
#

Hello, I spent some time on a little function:

here is the situtation:

I have a object containing Option (from fp-ts but whatever);

const input = {
  name: O.Some("Hello"),
  age: O.None(),
  gender: O.Some("M")
}

And i want to do 2 things, remove the key where value is "None", and map value where type if Some:

exemple output:

const output = {
  name: "hello",
  gender: "M"

I cannot make the type to match what i have in mind, everytime, the output is infered to uknown, or string, or something I don't want.
(runtime work, only typescript cause me troubles)

Here is my last attempt:

function excludeNoneFromObject<
  OldObject extends Record<string, O.Option<V>>,
  V extends string,
>(data: OldObject): Record<keyof OldObject, V> {
  return Object.fromEntries(
    Object.entries(data)
      .filter(([_, value]) => O.isSome(value))
      .map(([key, value]) => [key, O.toNullable(value)]),
  ) as Record<keyof OldObject, V>;
}

I feel it's too personal to post it on stackoverflow,
thanks in advance for your help

frank wyvern
#

Something along the lines of

type Resolve<T extends Record<PropertyKey, Some | None>> = {
    [K in keyof T as T[K] extends Some ? K : never]: T[K] extends Some<infer U> ? U : never;
}
#

I'm on mobile ATM, will send the full solution when home.

#

@willow moss

#

For the reference

hallow owlBOT
#
ascor8522#0

Preview:```ts
import { option } from "fp-ts";

//////

type Resolve<T extends Record<PropertyKey, option.Option<any>>> = {
[K in keyof T as T[K] extends option.Some<any> ? K : never]: T[K] extends option.Some<infer U> ? U : never;
}

//////

declare const O: {
Some<const T>(t: T): option.Some<T>;
...```

frank wyvern
#

@willow moss

willow moss
#

Thanks very much @frank wyvern , it is exactly what I was looking for. It's less trivial that I was thinking. Happy new year 🙏

frank wyvern
#

I wouldn't really consider than a complex type tho

#

it does the 2 steps you listed:

#
  • filtering out the None values
  • extract the generic value if Some<T>
#

don't hesitate to have a look at the article in the TS handbook, it's pretty well explained, and mapped types are everywhere in TS

willow moss
#

Thanks for your answer, I was not aware of mapped types.

It's probably not difficult, I come from other langage where meta programming have less features. I looked for mapped types, thanks for including resources.
If I can ask for help again, I changed a little bit your code: (since the value are some or none dynamically:

Obviously it doesn't work anymore because we can't assert the type. In my real code, values are constructed like: O.fromNullable(long?.chain?.of?.maybe?.value);

hallow owlBOT
#
ttncrch#0

Preview:```ts
import {option as O} from "fp-ts"

//////

type Resolve<
T extends Record<PropertyKey, O.Option<any>>

= {
[K in keyof T as T[K] extends O.Some<any>
? K
: never]: T[K] extends O.Some<infer U> ? U : never
}

//////

const input = {
name: O.of("a"),
age: O.none
...```

#
ascor8522#0

Preview:```ts
import {option as O} from "fp-ts"

//////

type Resolve<
T extends Record<PropertyKey, O.Option<any>>

= {
[K in keyof T as T[K] extends O.Option<infer U>
? U extends any
? K
: never
: never]: T[K] extends O.Option<infer U>
? U
: never
}

//////

const input =
...```

frank wyvern
#

@willow moss

#

it's basically the same idea

#

except Some and None have been replaced by Option<U> and Option<never>

#

so we need to extract the U or never part first, using infer, then make sure it's not never

#

also, don't forget to add as const when calling O.of and O.some, so that you keep the exact type of the litteral ("M" instead of string)