#Infer return type of generic function type

48 messages · Page 1 of 1 (latest)

neon moth
#

Is there a way to fix the following code so that the type of x is number?

type IdentityMap = <K>(arg: K) => K;

type InferredValueType<K, KtoV extends (arg: K) => any> = KtoV extends (
  arg: K
) => infer V
  ? V
  : never;

let x: InferredValueType<number, IdentityMap>;

In general, I want to represent a type-level function that maps types K to other types V

#

The reason I want to do this is that I'd like to have a wrapper around Map so that every (key, value) pair in the map has some configurable relationship between the types of key and value. E.g., keys could be constructor functions, and values could be instances of those functions, etc

#

So this would correspond to defining types other than IdentityMap that have more interesting relationships between the argument and return type

#

I guess this is related to higher kinded types?

empty bone
#

yeah, pretty much

#

if you have access to the value you can use an instantiation expression to apply type parameters:

split havenBOT
#
const identityMap = <K>(arg: K): K => arg
declare const x: ReturnType<typeof identityMap<number>>
//            ^? - const x: number
empty bone
#

but you can't do that if you only have the generic function's type

#

I'd like to have a wrapper around Map so that every (key, value) pair in the map has some configurable relationship between the types of key and value
this sounds interesting but i can't quite picture the implementation. if you can sketch up an example or something maybe i'll have other ideas

#

you can encode HKTs in typescript but it's not a first-class thing and the encodings are pretty awkward

#

!:hkt

split havenBOT
#
tjjfvi#0
`!t6:hkt`:

Here's the HKT encoding I often use:```ts
interface HKT {
input: unknown,
output: unknown,
}

type CallHKT<F extends HKT, I> =
(F & { input: I })["output"]

interface ArrayHKT extends HKT {
output: Array<this["input"]>
}

type X = CallHKT<ArrayHKT, number> // Array<number>

empty bone
#

☝️ there's one encoding, but there are others

neon moth
#

Sure let me see if I can come up with a concrete example. To be honest I'm trying to write something a lot more general than I really need, but it feels like a fun puzzle.

#

thank you for the responses!

empty bone
#

haha, i know that feeling

neon moth
#

ok, here's an example: ```typescript
type ClassOf<T> = { new (...args: any[]): T };

class ConstructorToInstanceMap {
private map: Map<any, any>;
constructor() {
this.map = new Map<any, any>();
}

set<Value>(key: ClassOf<Value>, value: Value) {
this.map.set(key, value);
}

get<Value>(key: ClassOf<Value>): Value | undefined {
let value = this.map.get(key);
if (value === undefined) return undefined;
return value as Value;
}
}

class ClassA {}
class ClassB {}

let map = new ConstructorToInstanceMap();
map.set(ClassA, new ClassA());

let a = map.get(ClassA);```

#

This map has the property that every key value pair has the property that the key is a class object and the value is an instance of that object, and given any key we can infer the type of the value (though admitedly I am twisting typescript's arm)

#

I'd like to have a general solution for this kind of thing where the relationship between the key and value types is something that can be configured

#

(e.g., maybe instead of having (key=class, value=class instance) pairs, maybe I want (key=class, value= message handlers for that class)

digital raft
#

yes, that is the exact situation in which HKTs are useful

neon moth
#

amazing. This is a puzzle I have thought on and off again for a few months (but I'm not too familiar with typescript and only use it casually, so progress is slow)

#

I'll see if I can get it working using the HKT example above

#

Ahh that's very nice. I feel like what I tried at the top of this chat with using a generic function type was pretty close

digital raft
#

it looks close, but that's not how typescript works

#

(it is how flow works, though!)

neon moth
#

Oh! What's flow?

neon moth
#

And yeah, not actually close to the right solution, but the right kind of idea. Have some type that is essentially a pair of input to output types

neon moth
#

😦

digital raft
neon moth
#

is flow considered an alternative to typescript?

#

I guess it doesn't modify the language?

digital raft
old flare
#

if you only care about react or node, sure, anything else you're on your own.

neon moth
#

ah

digital raft
#

the ecosystem is tiny, yeah

neon moth
#

ah I see

digital raft
neon moth
#

In the HKT pattern above, is there an easy way to impose a constraint on the input type?

#

e.g., the input must be a class object or something like that

#

I guess something like this works: ```typescript
interface ClassToInstanceHTK extends HKT {
output: this["input"] extends abstract new (...args: any[]) => any
? InstanceType<this["input"]>
: never;
}

#

anyway thank you. This is very satisfying and scrached a long itch I've had!

digital raft
#

interface HKT<I, O>

#

input: I
output: O