I would like to create a function f
that accepts a Box<T>
as its first argument, and a second argument of type T
that is optional if (and only if!) T extends null
.
Here is how one would call f
:
let _ = new Box(null)
let o = new Box({})
f(_, null) // ✅
f(o, {}) // ✅
f(_) // ✅
f(o) // ❌ (should not compile)
And here is how I have (attempted to) define f
:
class Box<V = any> {
constructor(public v: V) {}
}
type _ = null
type O = object
function f<V extends _>(b: Box<V>, v?: V): void
function f<V extends O>(b: Box<V>, v: V): void
function f(b: Box, v?: any) {}
This seems to work at first glance. But there's a problem! The error for f(o)
reports the following:
Argument of type 'Box<{}>' is not assignable to parameter of type 'Box<null>'.
When the error I want users of f
to actually see is:
Expected 2 arguments, but got 1.
It seems that TypeScript is preferentially selecting the overload where the second argument is optional. How do I make it select the other overload when T
does not extend null? Or otherwise achieve my goal?
CodePudding user response:
You can get the error you want if you use a conditional type in a rest parameter to basically change the optionality of the last argument based on V
:
function f<V>(b: Box<V>, ...a: V extends null ? [v?: V]: [v:V]): void
function f(b: Box, v?: any) {}