How can I make a functions argument required if the inferred type exists with keys, and optional if it does not exist or it is an empty object?
I've tried doing it with conditionals and with overloads but havent figure it out yet.
const MyFunc = <T extends {}>(vars?: T): void => {
// ...
}
// Should be allowed
MyFunc()
// Should be allowed
MyFunc<{}>()
// Should not be allowed - the argument should be required
MyFunc<{ a: string }>()
I've tried overloads and conditional types checking keyof T extends never ? ...
but Im stumped. Is this even possible with current TypeScript?
Thanks!
CodePudding user response:
Since you apparently want MyFunc
to always be a generic function with exactly one type parameter T
corresponding to the type of the vars
argument, the only place we can make this change is to the parameter types of your call signature. If you relaxed that restriction, then overloads and other approaches would probably give you similar effects, but that would be a different question.
My suggested solution looks like
const MyFunc = <T extends {}>(
...args: {} extends T ? [vars?: T] : [vars: T]
): void => { }
where instead of a single vars
parameter, we have an args
rest parameter whose type is a tuple type that depends conditionally on whether an empty object type is assignable to T
. If so, then the tuple has a single optional element of type T
and therefore vars
is optional. If not, then the tuple has a single required element and therefore vars
is required.
Let's test it out:
MyFunc(); // okay
MyFunc<{}>(); // okay
MyFunc<{ a: string }>(); // error!
MyFunc<{ a?: string }>(); // okay
Looks good. Note that since {}
is a valid value for {a?: string}
, then MyFunc<{a?: string}>()
is allowed.