#Function infer generic parameter key from input on another key

27 messages · Page 1 of 1 (latest)

cunning mortar
#

Here's an example, I would like to make the expect-error directives pass. I recall seeing a solution to this in the past, but can't remember now.

interface Input<T extends 'a' | 'b' = 'a' | 'b'> {
  t: T
  u: T extends 'a' ? (1|2) : (3|4)
}

const create = <$Input extends Input>(x: $Input) => x

// create({ t: 'a', u: /* should be 1|2 <- struggling to get this working */ })

create({t:'a',u:1})
create({t:'a',u:2})

// @ts-expect-error
create({t:'a',u:3})

// @ts-expect-error
create({t:'a',u:4})
thorny dew
#

You want a DU.

#

!hb discriminated union

random pathBOT
cunning mortar
#

Taking a look

#

I'm aware of DU. That doesn't fit my use-case. The relationship between 't' and 'u' is actually more complex than you see here.

#

My question is how to infer u from t in turn from the value passed to create

thorny dew
#

Show the more complex relationship then, I can only answer based on what you show.

cunning mortar
#

shifting the generic of create seems to be the thing to do for this.

thorny dew
#
const create = <T extends 'a' | 'b'>(x: Input<T>) => x

Would be an easy fix.

cunning mortar
#

But there's a problem with that, which is, I need the Input<T> available on the result type too

thorny dew
#

Well, I just wouldn't write Input<T> like that and use a DU.

cunning mortar
#

I'll give you the more complex example, sec.

random pathBOT
#
jasonkuhrt#0

Preview:```ts
//declare global {
interface Foo {
A: {
hasThing: true
}
}
//}
//declare global {
interface Foo {
B: {
hasThing: false
}
}
//}

type Name = keyof Foo

interface Input<$Name extends Name = Name> {
t: $Name
u: Foo[$Name] extends true ? (1|2) : (3|4)
...```

random pathBOT
#

@cunning mortar Here's a shortened URL of your playground link! You can remove the full link from your message.

jasonkuhrt#0

Preview:```ts
//declare global {
interface Foo {
A: {
hasThing: true
}
}
//}
//declare global {
interface Foo {
B: {
hasThing: false
}
}
//}

type Name = keyof Foo

interface Input<$Name extends Name = Name> {
t: $Name
u: Foo[$Name] extends true ? (1|2) : (3|4)
...```

thorny dew
#

Are you in control of Foo?

#

Actually, that doesn't really matter.

#

As a side note it seems like your code is wrong, I assume what you really want is Foo[$Name]['hasThing'] extends true not Foo[$Name].

random pathBOT
#
nonspicyburrito#0

Preview:```ts
interface Foo {
A: {
hasThing: true
}
B: {
hasThing: false
}
}

type Input = {
[K in keyof Foo]: {
t: K
u: Foo[K]["hasThing"] extends true ? 1 | 2 : 3 | 4
}
}[keyof Foo]

// CONSTRUCTOR

const create = <T extends Input>(x: T): T["u"] =>
...```

cunning mortar
#

Damn, thanks @thorny dew ! This is interesting...

thorny dew
#

Input is a DU, it's just generated from Foo.

#

You can hover over Input to see the generated output.

cunning mortar
#

Was about to say, you're back to DU but in a more dynamic/computed way

#

Yeah, and the autocomplete when using create works great too...