1

I've tried to apply this solution to disallow call of generic function with second type equal to any.

Following construction works until the first generic parameter is explicitely specified:

 declare function f<V = any, A extends ISmth = ISmth>(a: IfAny<A, never, A>): V;

In case that I need V to be first parameter and to be optional (it's return type and can't be inferred - it should iether be specified explicitely or be any), how can I fix the code?

I've tried to change default value, but it breakes any calls with explicit first type:

declare function g<V = any, A extends ISmth = any>(a: IfAny<A, never, A>): V;

Full code:

interface ISmth { x: number; }

type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;

declare function f<V = any, A extends ISmth = ISmth>(a: IfAny<A, never, A>): V;
declare function g<V = any, A extends ISmth = any>(a: IfAny<A, never, A>): V;

declare var x: ISmth, y: any;

f(x); // Fine
f(y); // Fine: Argument of type 'any' is not assignable to parameter of type 'never'.

f<string>(x); // Fine
f<string>(y); // BAD: Should be an error, but compiles

g(x); // Fine
g(y); // Fine: Argument of type 'any' is not assignable to parameter of type 'never'.

g<string>(x); // BAD: Should NOT be an error
g<string>(y); // Fine: Argument of type 'any' is not assignable to parameter of type 'never'.
Qwertiy
  • 19,681
  • 15
  • 61
  • 128

1 Answers1

5

There's currently no partial type argument inference in TypeScript, so this won't work for you. Workarounds are to make a curried function where you manually specify one type parameter and return a function whose type parameter is inferred:

declare const fCurry: <V = any>() => 
  <A extends ISmth = ISmth>(a: IfAny<A, never, A>) => V;
fCurry()(x); // no error
fCurry()(y); // error
fCurry<string>()(x); // no error
fCurry<string>()(y); // error

Or you make a function that takes a dummy argument for the type parameter you wish to manually specify... it can be null at runtime but you use a type assertion to let the compiler know what type you mean:

declare function fDummy<V = any, A extends ISmth = ISmth>(
  a: IfAny<A, never, A>, 
  dummy?: V
): V;
fDummy(x); // no error
fDummy(y); // error
fDummy(x, null! as string); // no error
fDummy(y, null! as string); // error

Neither way is perfect, but they have the desired behavior. Hope that helps. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360