TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

`T?` shortcut for `T | undefined`

Open streamich opened this issue 9 months ago • 10 comments

🔍 Search Terms

maybe type, optional type, or undefined

✅ Viability Checklist

  • [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
  • [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

⭐ Suggestion

Allow T? sugar syntax for T | undefined.

📃 Motivating Example

Often times some API returns a value or undefined, if the value does not exist. For example, consider this "map" interface:

interface Map<K, V> {
  get(key: K): V | undefined;
}

With this sugar syntax it would be possible to shorten it as folows:

interface Map<K, V> {
  get(key: K): V?;
}

Or, similarly in observables:

interface Observable<T> {
  getValue(): T?;
}

Or, built-in Array:

interface Array<T> {
  find<S extends T>(predicate): S?;
}

And many other places where ... | undefined is used.

💻 Use Cases

  1. Types which can be something specific or undefined.
  2. APIs which may retrieve nothing.

streamich avatar Mar 16 '25 20:03 streamich

its not same if value is there and set as undefined or if it is completely missing

Smrtnyk avatar Mar 16 '25 21:03 Smrtnyk

its not same if value is there and set as undefined or if it is completely missing

ofc

streamich avatar Mar 16 '25 21:03 streamich

Duplicate of #13184 / #7426 and others.

MartinJohns avatar Mar 16 '25 21:03 MartinJohns

We've gotten this far without it and I think this would create more confusion than it would solve; people varyingly suggest T? "obviously" means T | null | undefined, T | undefined, or T | null (e.g.); we're bound to aggravate at least 2/3rds of people with any given choice here.

RyanCavanaugh avatar Mar 17 '25 19:03 RyanCavanaugh

@RyanCavanaugh By far the most common use is T | undefined. There is some negligible use of T | null, and T | null | undefined is almost never used, and should not be used.

It should not aggravate anyone, as T | undefined is just so common pattern that everyone will find use for it. See, my example above Map, Set, Array, Observable (almost all standard libraries) use T | undefined.

streamich avatar Mar 18 '25 13:03 streamich

Have you read the linked issues? There was much contention. Admittedly those discussions were quite a while ago and I think we'd be closer to a consensus that T? should be equivalent to T | undefined, but I'm sure it would still be far from unanimous. Of course I think it would be great if we could have a T? shorthand, but there is no ideal choice for what it means, and we know we can get along fine without it.

snarbles2 avatar Mar 18 '25 14:03 snarbles2

@snarbles2 The contention was due to the fact that people expressed their opinions on what the T? should be in their mind.

My argument is that, there is A LOT of T | undefined in actual production code, libraries, JS built-ins. And that T | undefined deserves a shorthand. Almost all data structure libraries and built-ins use T | undefined for potentially missing values, almost all synchronous retrieval-of-something APIs use T | undefined for potentially non-retrievable things.

My, proposal is to only consider T | undefined. I do not suggest to even consider T | null or T | undefined | null in this discussion, as T | null is rarely used; and T | undefined | null even less so and is a code smell.

streamich avatar Mar 18 '25 14:03 streamich

If you're writing x in a parameter position, then x: T | undefined is almost always worse than x?: T. Same for properties. For local variables there's no equivalent, but that's comparatively rarer, and you usually don't need an annotation there.

Given that ? in the name position means | undefined, there's an expressiveness argument that ? in a type position ought to mean something useful; if you wrote

function foo(x?: T) {

then

function foo(x?: T?) {

should mean something difference, since you wrote a character you didn't need to. That implies either | null or | null | undefined

RyanCavanaugh avatar Mar 18 '25 16:03 RyanCavanaugh

If you're writing x in a parameter position, then x: T | undefined is almost always worse than x?: T.

I disagree. I think x: T | undefined is almost always better, as performance-wise makes all calling sites call the function with the same number of arguments.

Same for properties.

Again, disagree. Often it is useful to force to set class instance properties to undefined, so that they are explicitly initialized, so that performance-wise the JS engines at instance initialization know about that property:

class A {
  prop: Something | undefined; // Forces to initialize, so no surprises for JS engine.

  // or
  constructor(
    public prop: Something | undefined // Explicit and monomorphic good for performance
  ) {}
}

For local variables there's no equivalent, but that's comparatively rarer, and you usually don't need an annotation there.

It is a very common type for a local variable to "uninitialized", or undefined because, say, iterator finished. For example:

let curr: Something | undefined;

// ...

while (curr) {
  // ...
}

Another example:

let result: Something | undefined;

if (condition) {
  result = ...
} else {
  result = ...
}

return result!;

Given that ? in the name position means | undefined, there's an expressiveness argument that ? in a type position ought to mean something useful; if you wrote

function foo(x?: T) {

then

function foo(x?: T?) {

should mean something difference [..]

That is fine. Otherwise, following this logic | undefined should not be allowed as well, because it is extra "| undefined" characters, but from the expressiveness argument it should mean something useful:

function foo(x?: T | undefined) {

All of those are opinions on how to write code in those specific instances. But my argument is, regardless of anyones opinion which way is better: There are many instances of T | undefined in real code, would be nice to have a shortcut for it.

streamich avatar Mar 18 '25 17:03 streamich

The contention was due to the fact that people expressed their opinions on what the T? should be in their mind.

Well... yeah, that's how this works. And you can see how this discussion is going, and so hopefully you can see why this proposal was never adopted. It could be, and I'm not saying it shouldn't be. I would like it to be. But I can understand why it's very unlikely to happen.

snarbles2 avatar Mar 18 '25 17:03 snarbles2

This issue has been marked as "Declined" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

typescript-bot avatar Mar 21 '25 01:03 typescript-bot

Another case for undefined is that Optional Chaning operator returns undefined, when the accessed value is nullish.

streamich avatar Mar 31 '25 13:03 streamich