Partial type argument inference in typescript and workarounds for it

Nandin Borjgin
4 min readJun 19, 2019

--

Typescript has a powerful generic type system that empowers programmers to craft really reusable components. Regretfully, partial type argument inference has not been shipped at the time of this writing (there is a pull request planned to be investigated in the future though). And as the needs for use of it arise now and then in my projects, I come up with the idea that I might put the workarounds I have used down, in case someone else is also in urgent need of this feature.

Partial type argument inference

You need to have some basic understanding about generics in typescript to proceed.

Generic arguments of a generic function can be specified in two manners when the function gets called: inferred implicitly from the context or provided explicitly by the programmer.

Inferring type arguments implicitly

Typescript, as many other modern languages, is very clever at inferring type information from the context.

Multiple type arguments can also be inferred as long as the context is comprehensive.

Providing type arguments explicitly

As implicit type inference is always an optional feature, type arguments can be provided explicitly in the function call.

Usecase that needs partial inference

While implicit inferring is sufficient under most circumstances and providing type arguments explicitly brings nothing but verbosity, explicit provision, especially partial provision, is indispensable in some cases.

modify is a function that adds a type-safe method on a given target. Given a string literal key and a type U, it adds a method key of signature (value: U) => void.

And the preferred usage of modify function is:

However, the typescript compiler of the time of this writing (v3. 5) emits an error on line 2: Expected 3 type arguments, but got 1.

This is because typescript compiler only allows 2 patterns regarding a generic function call: either provide all type arguments explicitly or delegate inferring task entirely to the compiler. So, the compiler-happy usages are:

What we actually want to do here is to explicitly provide the concrete type for U (which is number in this example) and let the compiler infer the other two, T and K. In other words, we want the compiler to partially infer the concrete type for T and K, hence “partial type argument inference”.

Partial type inference is available in some other languages, like Swift, but does not available in typescript for now (I believe the pull request would be landed in a foreseeable future though). And I’ll present, in the next section, two workarounds I’ve been using to correctly type the returned value while reducing the verbosity as much as possible.

Workarounds

Fluent API

Since the root of the problem here is that typescript compiler does not allow providing partial type arguments, we can separate the type variables that needs to be explicitly specified from the ones that can be inferred from the context.

The reasoning here is: U only serves as a type indicator of the return value, and that means U does not have any impact on the function implementation. So we can lift it to a dummy wrapper function just to make the type system to be able to carry it to the return value.

The downside of this workaround is the need for an extra pair of parentheses. It makes the api uglier and confusing.

Placeholder

If we look at the problem from a different perspective, its nature can also be stated as there is no context from which the compiler can infer the concrete type for U. The solution is almost apparent: just add the missing context.

Personally, I prefer this approach over the fluent api for its elegance and intuitiveness. Writing down modify(John, 'age', <number>_) is just like saying “Modify John to add a method ‘age’ which accepts some number”. I’m reading the underscore as “some” here, but you can read it as “any” if you prefer.

As all the hacky workarounds, this one also comes with a drawback: It cannot be used when ‘JSX’ is enabled in the project configuration (either in tsconfig.json or command line arguments)

when using TypeScript with JSX, only as-style assertions are allowed

Typescript Handbook

Last words

The pull request I mentioned at the beginning is likely to get landed and then all the workarounds in this article would be obsolete. And I hope these workarounds can help you relive the pain before the PR gets landed.

That’s all, thanks for reading and I hope this post is worth your time.

👏 are sincerely appreciated.

--

--