Magnus
Magnus

Reputation: 3712

Typescript: how to REQUIRE the specification of types when calling a function?

I am declaring a typed function in typescript. Is there a way to define a function such that the specification of types is required when that function is called? I.e. I want something like the following:

function MyFunction<T with some fancy TS syntax>(type: T){
   ...
}

MyFunction('xxx'); // TS error

MyFunction<string>('xxx'); // No TS error

Upvotes: 0

Views: 326

Answers (1)

jcalz
jcalz

Reputation: 327624

You can do a few things to get behavior like this but there might well be edge cases and unexpected side effects because the compiler is supposed to infer type parameters this way.


One step toward a possible solution is to find a way to use a type parameter inside a function signature without letting the compiler use it to infer that parameter: called "non-inferential type parameter usage". This feature isn't directly supported, but there's an open issue in GitHub requesting it: microsoft/TypeScript#14829. The idea is that there'd be a NoInfer<T> which is the same as T but does not participate in type inference; for your case you'd want to have the type function parameter be annotated as NoInfer<T>.

Anyway in the above issue I suggested that one could (ab)use conditional types to make a NoInfer:

type NoInfer<T> = [T][T extends any ? 0 : never];

That eventually evaluates to T, but the conditional type T extends any ? 0 : never is deferred until T is known, which happens after type inference.


The next step would be to give T a default value that's not compatible with any reasonable value you can pass in. So when inference fails to happen because of NoInfer<T>, it becomes this "bad" default. We can use never for that because it is assignable to all types but nothing (other than itself) is assignable to it.

That gives us:

function MyFunction<T = never>(type: NoInfer<T>) { }

Let's see if it works:

MyFunction('xxx'); // error
// ------> ~~~~~
// Argument of type '"xxx"' is not assignable to parameter of type 'never'

MyFunction<string>('xxx'); // okay

MyFunction<string>(123); // error
// --------------> ~~~
// Argument of type '123' is not assignable to parameter of type 'string'

That looks like the behavior you want. The error message that "xxx" is not assignable to never is a bit obscure, in that someone who reads it is unlikely to know what to do to fix it, so you'd have to document your function pretty well. Or we could wait for or fake up an "invalid" type suggested in microsoft/TypeScript#23689 to customize the error message. Faking it up is kind of ugly (even more than the previous stuff) so I won't go down that route unless someone really needs to see it. (There are comments in that issue about how to do it.)


Anyway, that's the closest I can imagine to doing what you want given the current state of TypeScript (3.9). Caveat emptor and good luck!

Playground link to code

Upvotes: 3

Related Questions