Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: express optional parameter in generic

Apparently in typescript there's a difference between optional parameter and parameter of union type with undefined

function test(s: string|undefined) {}

function test2(s?: string) {}

test() // error: An argument for 's' was not provided
test2() // OK

I want to declare a generic function that depending on the provided type would have its parameter optional or required. For example:

// Intention: if type is the same as its Partial counterpart then the parameter can be made optional
type MaybeOptional<T> = Partial<T> extends T ? T|undefined : T

type O = {
  name?: string
}

function generic<T extends any>(t: MaybeOptional<T>) {}

generic<O>({name:""})
generic<O>() // error. An argument for 't' was not provided.

How can I express such intention in Typescript? Can I assign this 'optional' trait through generics?

like image 591
Роман Чистоходов Avatar asked Oct 27 '25 02:10

Роман Чистоходов


1 Answers

You could use tuples in rest/spread positions to talk about a function's argument list in a more flexible way:

function generic<T extends any>(...args: Partial<T> extends T ? [t?: T] : [t: T]) { }

If, when you call generic<T>(), the type T is all-optional and Partial<T> extends T, this reduces to (...args: [t?: T]) => void, meaning "a list of arguments whose length is one and whose one element is optional and of type T", which reduces to (t?: T) => void). If, on the other hand, the type T is not all-optional, then this reduces to (...args: [t: T])=>void; the same as before except the element is required, which reduces to (t: T) => void.

Let's test it:

type O = {
  name?: string
}
generic<O>({ name: "" }); // okay
generic<O>({}); // okay
generic<O>(); // okay

type R = {
  name: string;
}
generic<R>({ name: "" }); // okay
generic<R>({}); // error
generic<R>(); // error

Looks good!

Playground link to code

like image 54
jcalz Avatar answered Oct 28 '25 18:10

jcalz