Why isn't "string?" allowed as a function return value in TypeScript?

Quick notes on optional parameters and properties in TypeScript

Why isn't "string?" allowed as a function return value in TypeScript?

Photo by Evan Dennis on Unsplash

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.

SomeThing? is a TypeScript notation. The ? suffix in TypeScript is equivalent to that thing being optional.

A declaration that includes this optional specifier translates to two states: either the declaration is defined or it is undefined.

This meaning can be further "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, or if it is defined, it is a string. Because it is possibly NOT defined, you don't need to specify it (assign something to it) when you instantiate an object of that type. I.e., it can be that the property does NOT exist! That is what undefined means!

If you instead declare it as property: string | undefined —which confusingly is also what the TypeScript compiler "translates" the previous notation to if you check the type hint— then you have to specify (assign) a value or undefined to it when you instantiate an object of that type. The property itself is defined and present on the resulting object but will not contain a value if you assigned undefined to it (which is a primitive value in JS, like I said before).

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! These 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 properties

Optional parameters and rest parameters

The TypeScript Handbook

I hope this short-ish article helped you understand TypeScript better. If not, leave a comment and I will try to improve it.