Photo by Evan Dennis on Unsplash
Why isn't "string?" allowed as a function return value in TypeScript?
Quick notes on optional parameters and properties in TypeScript
I've recently came upon this question on r/typescript: Why isn't "string?" allowed as a function return value, but instead it needs to be defined like "string | undefined"? and I thought I should blog about this too as I have pondered this question myself in the past. What follows is more or less what I've replied on Reddit.
So why is string?
an invalid notation?
You should first know that undefined
, like string
(not to be confused with the String
object) is a primitive JavaScript type.
SomeProperty?
is a TypeScript notation. The ?
suffix in TypeScript means to be optional.
This meaning is then translated based on its use.
If you declare a property on a TypeScript type or interface as property?: string
, it means that the property is either not defined at all, or if it is defined, it is a string
. Because it is possibly not defined, you don't need to specify it when you instantiate an object of that type.
If you instead declare it as property: string | undefined
, then you have to declare and assign a value or undefined
to it when you instantiate an object of that type. The property itself is declared and present on the resulting object but may not contain a value if you assigned undefined
to it.
You can't forget to assign something to a property: string | undefined
, like you can forget to return something from a function and it implicitly returns undefined
. The TypeScript compiler won't allow it.
So... you cannot declare function argument types, function return types and variable types as optional! The types themselves are NEVER optional! It is properties only that can be optional!
Examples
Here are some examples of invalid syntax of optional types and the current TS compiler errors (v4.8.4, some are misleading, I know 🤷♂️):
const opConst: string? = ""; // JSDoc types can only be used inside documentation comments.(8020)
function f(x: string?) {} // JSDoc types can only be used inside documentation comments.(8020)
function g(x: string): string? {
return undefined;
} // '{' or ';' expected.(1144)
interface IFace {
IsThisMeantToBeOptionalOrWhat: string?
} // JSDoc types can only be used inside documentation comments.(8020)
And here are the correct versions:
const opConst: string | undefined = "";
function f(x: string | undefined) {} // required param that can be explicitly assigned undefined
function f(x?: string) {} // optional param defaulting to undefined
function g(x: string): string | undefined {
return undefined;
}
interface IFace1 {
NotOptional: string | undefined
}
interface IFace2 {
Optional?: string
}
Pay close attention to the last two interface declarations.
In the first interface, the property is not optional, it must exist but its value can be undefined
, and whenever it is undefined
, it must be assigned that value explicitly.
In the second interface, the property is completely optional, meaning that you can omit to declare it in object initializations. The resulting type though is the same as in the first interface, a string | undefined
.
I guess what you gain here is the possibility to omit the allocation of memory for another property on an object, which may or many not be used.
References
When in doubt about TypeScript, always check the handbook first.
Optional parameters and rest parameters
I hope this short article helped you understand TypeScript better. If not, leave a comment and I will try to improve it.