#How to transform optional key into undefined value?

105 messages ยท Page 1 of 1 (latest)

tired streamBOT
#
onkeltem#0

Preview:```ts
type A<T> = {
[K in keyof T]-?: T[K]
}

type Bar = {
name?: string
}

type ResBar = A<Bar>
// ^?

type ResBarExpected = {
name: string | undefined
}```

solar lion
#

I need to get ResBarExcected type

#

But I don't know how to change {name?: string} => {name: string | undefined}

#

I need to turn this:

type Foo = {
  a1?: {
    b1?: {
      c1?: string
    }
  }
}

into this:

type FooRes = {
  a1: {
    b1: {
      c1: string | undefined
    }
  }
}
#

to not write question marks a1?.b1?.c1

gleaming sphinx
#

You can check for optional keys with extends

#

something like O extends { K?: unknown } for each key

#

So you'd put there where you have T[K] as a conditional

solar lion
#

But what is O?

gleaming sphinx
#

T

#

I use O for object

#

You are using T

solar lion
#

Do you mean [K in keyof T]: T extends { K?: unknown } ? 1 : T[K]?

gleaming sphinx
#

something like that

solar lion
#

Nah, it doesn't get undefined ๐Ÿ˜ฆ

tired streamBOT
#
type E1 = { a: string } extends { a?: string } ? true : false
//   ^? - type E1 = true
type E2 = { a?: string } extends { a: string } ? true : false
//   ^? - type E2 = false
type E3 = {} extends { a?: string } ? true : false
//   ^? - type E3 = true
type E4 = { a?: string } extends {} ? true : false
//   ^? - type E4 = true
gleaming sphinx
#

huh

solar lion
#

wait K?: is wrong

gleaming sphinx
#

What is your code?

#

yeah

#

[Key in K]?: unknown

#

I think

tired streamBOT
#
onkeltem#0

Preview:```ts
type A<T> = {
[K in keyof T & string]: T extends {
[K2 in K]?: unknown
}
? T[K] | undefined
: T[K]
}

type Bar = {
name?: string
}

type ResBar = A<Bar>
// ^?

type ResBarExpected = {
name: string | undefined
}```

gleaming sphinx
#

Good?

solar lion
#

Still doesn't work. Not it stopped reacting on the optionality of name

#

So both
type Bar = {
name?: string;
}
and
type Bar = {
name: string;
}
make the same string | undefined output

tired streamBOT
#
type A<T> = {
  [K in keyof T & string]: T extends { [K2 in K]?: unknown } ? T[K] | undefined : T[K]
}

type Bar = {
  name?: string;
}

type ResBar = A<Bar>
//    ^? - type ResBar = {
//        name: string | undefined;
//    }


type ResBarExpected = {
  name: string | undefined
}
gleaming sphinx
#

ah

gleaming sphinx
#

I'm pretty sure there is a way along those lines

solar lion
#

I hope!

tired streamBOT
#
sandiford#0

Preview:```ts
type A<T> = {
[K in keyof T & string]: T extends {
[K2 in K]: unknown
}
? T[K]
: T[K] | undefined
}

type Bar = {
name?: string
}

type ResBar = A<Bar>
// ^?

type Baz = {
name: string
}

type ResBaz = A<Baz>
// ^?

type ResBarExpected = {
name: string | undefined
...```

solar lion
#

It must be it!

#

Thanks @gleaming sphinx ๐Ÿ™‚

tired streamBOT
#
sandiford#0

Preview:```ts
type A<T> = {
[K in keyof T & string]: { [Key in K]: unknown } extends T ? T[K] | undefined : T[K]
}

type Bar = {
foo: string
name?: string;
}

type ResBar = A<Bar>
// ^?

type Baz = {
foo: string
name: string;
}

type ResBaz = A<Baz>
// ^?
...```

gleaming sphinx
#

also works

solar lion
#

Looks a little less clear

#

{ [Key in K]: unknown } extends T - why undefined then?

gleaming sphinx
#

Oh

solar lion
#

Current version:

type A<T> = {
  [K in keyof T]-?: T extends { [K2 in K]: unknown } ? T[K] : T[K] | undefined;
};
gleaming sphinx
#

I didn't mean to write it that way, it worked by accident : )

#

{ [K2 in K]: unknown } this makes keys required. So if the key is optional it changes the type and causes T not to extend, I guess

#

Can we make a mapped type that preserves optionality?

tired streamBOT
#
type O = {
  a: string,
  b?: string
}

type O2 = { [K in keyof O]: O[K] }
//   ^? - type O2 = {
//       a: string;
//       b?: string | undefined;
//   }


type O3 = { [K in keyof O]: { [Key in K]: O[Key] } }
//   ^? - type O3 = {
//       a: {
//           a: string;
//       };
//       b?: {
//           b: string | undefined;
//       } | undefined;
//   }
gleaming sphinx
#

b in O2 is optional

#

but b loses optionality at O2.b.b

#

I thought it would preserve that, and I could use that in the check

solar lion
#

Here is another issue with undefined

tired streamBOT
#
onkeltem#0

Preview:```ts
type Val<T> = {
value: T
}

type A<T> = {
[K in keyof T]-?: T extends {[K2 in K]: unknown}
? Val<T[K]>
: Val<T[K] | undefined>
}

type Res = A<{foo: string}>
// ^?```

solar lion
#

Even if I pass Val<T[K] | undefined>
then Val returns or takes just string

#

skipping undefined

#

I would expect the Res to be:

{ foo: Val<string | undefined>}
#

but it's just

{foo: Val<string>}
#

Here it works I want it to work:

#

!ts

tired streamBOT
#
type Foo<T> = {
  value: T
}

type aaa = Foo<string | undefined>
//   ^? - type aaa = {
//       value: string | undefined;
//   }


gleaming sphinx
#

huh

gleaming sphinx
tired streamBOT
#
sandiford#0

Preview:```ts
type Val<T> = {
value: T
}

type A<T> = {
[K in keyof T]-?: T extends {[K2 in K]: unknown}
? Val<T[K]>
: Val<T[K] | undefined>
}

type Res = A<{foo: string}>
// ^?

const r: Res = {
foo: {value: "string"},
}
const r2: Res = {
foo: {value: undefined},
...```

gleaming sphinx
#
type Res = A<{foo: string}>
#

your mistake here?

solar lion
#

oh, right!

#

But it doesn't work anyway in my real sandbox ๐Ÿ™‚

#

Ok, nevermind, prob I did something else wrong

gleaming sphinx
#

If you don't make mistakes, you are not a programmer ๐Ÿ˜„

tired streamBOT
#
onkeltem#0

Preview:```ts
export type Scalar = string | number | boolean | bigint

type DescribeScalar<T> = {
value: T
}

type DescribeThing<T> = T extends Scalar
? DescribeScalar<T>
: never

type DescribeObject<T> = {
[K in keyof T]-?: T extends {[K2 in K]: unknown}
? DescribeThing<T[K
...```

solar lion
#

Ok, so here is the thing

#

becasue I do: T extends Scalar it leaves only string and loses undefined

#

So in this line:

type DescribeThing<T> = T extends Scalar ? DescribeScalar<T> : never;

T equals to string | undefined

#

The reason is - undefined is not Scalar, right?

gleaming sphinx
#

what is Scalar?

#

oh

solar lion
#

Now if I add | undefined like this:

type DescribeThing<T> = T extends Scalar | undefined ? DescribeScalar<T> : never;

the result becomes:

type A = {
    foo: DescribeScalar<undefined> | DescribeScalar<undefined> | DescribeScalar<string>;
}
#

So two times undefined and one - string ๐Ÿ™‚

gleaming sphinx
#

that's funny

solar lion
#

This one seems to be a little better:

#
type DescribeThing<T> = T extends undefined ? DescribeScalar<undefined> : DescribeScalar<T>;
#

In this case the result is:

type A = {
    foo: DescribeScalar<undefined> | DescribeScalar<string>;
}
tired streamBOT
#
sandiford#0

Preview:```ts
export type Scalar = string | number | boolean | bigint

type DescribeScalar<T> = {
value: T
}

type DescribeThing<T> = [T] extends [
Scalar | undefined
]
? DescribeScalar<T>
: never

type DescribeObject<T> = {
[K in keyof T]-?: T extends {[K2 in K]: unknow
...```

gleaming sphinx
#

!hb distribution

tired streamBOT
solar lion
#

But in the end I would prefer to have just: DescribeScalar<string | undefined>

solar lion
#

Yes yes yes!

#

I observed this trick with square brakets at the bottom of my consciences but couldn

#

t fully remember it ๐Ÿ™‚

gleaming sphinx
#

The double undefined feels like it could be a bug

solar lion
#

yeah, but a lesser one, barely makes a difference ๐Ÿ™‚

gleaming sphinx
#

only occurs with eOPT

solar lion
#

I switched it on because it's easier to debug with it. Unfortunately we have if off in our projects ๐Ÿ˜ฆ

tired streamBOT
#
sandiford#0

Preview:```ts
type DescribeThing<T> = T extends
| string
| number
| undefined
? {value: T}
: never

type DescribeObject<T> = {
[K in keyof T]-?: T extends {[K2 in K]: unknown}
? DescribeThing<T[K]>
: DescribeThing<T[K]>
}

type B = DescribeObject<{foo?: string}>
...```

solar lion
#

I wished I knew about it before

gleaming sphinx
#

Its really weird

tired streamBOT
#
type DescribeThing<T> = T extends (string | number | undefined) ? { value: T; } : never;

type DescribeObject<T> = {
  [K in keyof T]-?: T extends { [K2 in K]: unknown }
    ? DescribeThing<T[K]>
    : DescribeThing<T[K]>;
};


type B = DescribeObject<{ foo?: string }>;
//   ^? - type B = {
//       foo: {
//           value: undefined;
//       } | {
//           value: string;
//       };
//   }

type C = DescribeThing<string>
//   ^? - type C = {
//       value: string;
//   }
#
type DescribeThing<T> = T extends (string | number | undefined) ? { value: T; } : never;

type DescribeObject<T> = {
  [K in keyof T]-?: DescribeThing<T[K]>;
};

type B = DescribeObject<{ foo?: string }>;
//   ^? - type B = {
//       foo: {
//           value: undefined;
//       } | {
//           value: string;
//       };
//   }
#
type DescribeThing<T> = T extends (string | number | undefined) ? { value: T; } : never;

type DescribeObject<T> = {
  [K in keyof T]: DescribeThing<T[K]>;
};

type B = DescribeObject<{ foo?: string, bar?: number }>;
//   ^? - type B = {
//       foo?: {
//           value: undefined;
//       } | {
//           value: string;
//       } | undefined;
//       bar?: {
//           value: undefined;
//       } | {
//           value: number;
//       } | undefined;
//   }
gleaming sphinx
#

huh

solar lion
#

@gleaming sphinx are you here? ๐Ÿ™‚

#

!ts

tired streamBOT
#
type Transform<T> = {
  [K in keyof T]-?: T extends { [K2 in K]: unknown } ? T[K] : T[K] | undefined;
};

type Foo = {
  foo?: {
    bar: string;
  };
};


type Tranform_Foo = Transform<Foo>;
//   ^? - type Tranform_Foo = {
//       foo: {
//           bar: string;
//       };
//   }
solar lion
#

Why it's losing undefined? ๐Ÿ™‚

tired streamBOT
#
onkeltem#0

Preview:```ts
type Transform<T> = {
[K in keyof T]-?: T extends {[K2 in K]: unknown}
? T[K]
: T[K] | undefined
}

type Foo = {
foo?: {
bar: string
}
}

type Tranform_Foo = Transform<Foo>
// ^?```

solar lion
#

T[K] | undefined behaves like there were no undefined

#

Shouldn't it be:

foo: {
  bar: string;
} | undefined;
gleaming sphinx
#

You need eOPT on