1

I'm trying to have a function fun declared in TypeScript that uses its type parameter T as a constraint for what keys can be used as a parameter of another function context.Foo:

interface Context<T> {
    Foo(key: keyof T)
}

function fun<T>(provider: (context: Context<T>) => T): number {
    return 1
}

fun<{a: number}>(context => {
    return {a: context.Foo('a')}
})

So far so good, I can only use 'a' literal as a key parameter of context.Foo in the foo invocation at the bottom, because this invocation has a type parameter that declares T to be {a: number}.

What bothers me is that I actually define the structure of this type inline as a provider function body. I can only return an object that matches {a: number} definition.

What I'd like to achieve is to get rid of the need to explicitly define the T type of fun at invocation time and let type inference figure this type out by itself by the structure of the object I'm returning from the fun's parameter (that is required to be T), like this:

fun(context => {
    return {a: context.Foo('a')}
})

Unfortunately, this ends with an error:

TS2345: Argument of type '"a"' is not assignable to parameter of type 'never'.

Type inference for T failed and have fallen back to never which is definitely not useful in this case.

Is it possible to define this function in such a way that explicit setting of fun's T is not needed?

3
  • What's the return type of Context.Foo? Is it always the same type or is it generic? Commented Mar 20, 2018 at 8:02
  • I don't think this is possible, even with the up-coming infer. Commented Mar 20, 2018 at 8:11
  • @fathyb It might be the same type, say string. Commented Mar 20, 2018 at 8:30

1 Answer 1

1

I don't think is any way to achieve this, as soon as you try to tie Context<T> to the keys of the result, the compiler gives up in inference.

Since Foo always returns the same value type (at least from what I understood in the comments) you could specify just the keys of the type, and not have to specify the whole type:

interface Context<K> {
    Foo(key: K): number;
}

function fun<K extends string>(provider: (context: Context<K>) => Record<K, number>): Record<K, number> {
    return provider(null as any);
}

let a = fun<'a'>(context => {
    return { a: context.Foo('a') }
})
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.