Home > Net >  Make a function argument optional if the inferred type is an empty object
Make a function argument optional if the inferred type is an empty object

Time:11-23

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.

Playground link to code

  • Related