Reputation: 14539
Suppose we need to implement a method which returns something, but when this something is undefined
, it should return some default value passed in instead. But we don't want to make the default value param required, so are declaring it as an optional one:
private returnSomething(someParam: string, defaultValue?: string) : string | undefined {
const result: string | undefined = ...; // Some result-producing logic
return result !== undefined ? result : defaultValue;
}
And we expect the following to compile in strict mode (notice something1: string
type which is declared without the undefined
part):
const something1: string = this.returnSomething("someParam1", "?"); // Error, no undefined in the type of something1
const something2: string | undefined = this.returnSomething("someParam2");
Is it possible to declare a method in TypeScript so that when an optional parameter is not passed the return type of the method is considered as string | undefined
whereas when it's passed, the return type becomes string
?
Upvotes: 1
Views: 102
Reputation: 21881
So if I got you correctly, this is easy with a generic type:
const returnSomething = <D extends string | undefined>(someParam: string, defaultValue?: D) : D extends undefined ? string | undefined : string => {
const result = {} as any;
return result !== undefined ? result : defaultValue;
}
See the playground
UPD to avoid error when using without any
:
const returnSomething = <D extends string | undefined, R extends D extends undefined ? string | undefined : string>(someParam: string, defaultValue?: D) : R => {
const result = 'str';
return (result !== undefined ? result : defaultValue) as R;
}
UPD2. The correct approach (and my apologies for messing this up).
I want to give my apologies because I've introduced generics and conditional types, when they were unnecessary. I mean, I didn't know they can cause such an error. But I also was misguided by the original question. So the normal way, how I would make it, looks like this:
const returnSomething = (someParam: string, defaultValue?: string): string | undefined => {
const result = '';
return result !== undefined ? result : defaultValue;
}
const something1 = returnSomething("someParam1", "?");
const something2 = returnSomething("someParam2");
That's it. And yes, you should not assign types to something1
and something2
. Instead, you should runtime-check (and typescript infers types from the checking):
if (typeof something1 === 'undefined') {
/* oops the value is undefined, do something to make the program continue working properly */
}
else {
something1 // has type "string"
}
Regarding the error, I've submitted an issue.
Upvotes: 2
Reputation: 6466
Nurbol was most of the way there, I had to make one additional change to get it fully working:
const returnSomething = <D extends string | undefined>(someParam: string, defaultValue?: D) : D extends undefined ? string | undefined : string => {
const result = {} as any;
return result !== undefined ? result : defaultValue;
}
const something1: string = returnSomething("someParam1", "d"); // No error
const something2: string | undefined = returnSomething("someParam2"); // No error
const something3: string = returnSomething("someParam2"); // Yes error
const something4: string = returnSomething("someParam2", undefined); // Yes error
We need to define D
to accept undefined
initially, otherwise the conditional will always return just string
.
The side effect of this though is that you are now allowed to pass undefined
as a default value. It still errors as expected though.
You could also do this with function overloads:
function returnSomething(param: string): string | undefined;
function returnSomething(param: string, defaultValue: string): string;
function returnSomething(param: string, defaultValue?: string) {
const result = {} as any;
return result !== undefined ? result: defaultValue;
}
This avoids the undefined
issue.
Upvotes: 1