Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A way to disable "type argument inference" in generics?

Tags:

typescript

I was hoping that giving a default value to a generic will take precedence over type inference, but it doesn't:

// placeholder for a real express response.send
const sendResponse =  (x) => console.log(x);

function sendResult<T = never>(send: any, result: T) {
  send(result);
}

// I want to use this always with a type
type Num = { x: number };
sendResult<Num>(sendResponse, { x: 1 });
sendResult<Num>(sendResponse, { x: 'sss' }); // correctly showing error

// When I don't supply type, I'd like an error.
// But, T gets inferred instead of defaulting to never... so, no error :-(
sendResult(sendResponse, {x: 1})

See demo

Is there a way to make sure an error is thrown if generic is not provided?

Typescript version: 3.5.2

like image 427
Stepan Avatar asked Jun 20 '19 14:06

Stepan


People also ask

What happens when you call a generic procedure with no arguments?

Normally, when you call a generic procedure, you supply a type argument for each type parameter that the generic procedure defines. If you do not supply any type arguments, then the compiler attempts to infer the types to be passed to the type parameters.

What is type inference and generic methods in Java?

Type Inference and Generic Methods. Generic Methods introduced you to type inference, which enables you to invoke a generic method as you would an ordinary method, without specifying a type between angle brackets.

What is a type inference conflict?

Type argument inferred from the argument passed to parameter '<parametername1>' conflicts with the type argument inferred from the argument passed to parameter '<parametername2>'. A generic procedure is called without any type arguments, and the attempted type inference has produced a data type conflict among the type parameters.

Do we need to specify the value of the type argument?

Thus, in Java SE 7, you must specify the value of the value of the type argument as follows: This is no longer necessary in Java SE 8. The notion of what is a target type has been expanded to include method arguments, such as the argument to the method processStringList.


1 Answers

I don't understand the use case, since disabling behavior that TypeScript users expect is probably going to cause confusion. But you're the boss.

There is an existing GitHub suggestion at microsoft/TypeScript#14829 to allow a developer to indicate that a particular use of a generic type parameter should not be used for inference. There is no official support for this, but I think we can simulate it by taking advantage of the compiler's behavior when looking at conditional types that depend on an unresolved generic type parameter:

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

That is essentially a no-op on T, since any value you plug in (say string) will come out the other side: [string][string extends any ? 0 : never] becomes [string][0] becomes string. But the compiler sees T extends any and decides to defer the calculation until after T gets resolved. In particular, it can't use the value to infer T from that.

So then we can try

function sendResult<T = never>(send: any, result: NoInfer<T>) {
  send(result);
}

And see how it behaves:

type Num = { x: number };
sendResult<Num>(sendResponse, { x: 1 }); // okay
sendResult<Num>(sendResponse, { x: "sss" }); // error

sendResult(sendResponse, { x: 1 }); // error

The first two cases work as you want because specifying Num gives T a definite value, and NoInfer<Num> is just Num. In the last case, the compiler really has no viable inference site for T and thus it has to fall back to the default never, and you get your desired error.

Playground link to code

So that works as you want, but remember that you might want to rethink this idea, since people who get that error might be confused about what the heck they're supposed to do to make the error go away. People rely on type inference, so you'll need some great documentation if you proceed with this.

like image 185
jcalz Avatar answered Sep 18 '22 11:09

jcalz