Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shorter dot syntax to access enum values #45956

Closed
3 of 5 tasks
FilipeBeck opened this issue Sep 19, 2021 · 17 comments
Closed
3 of 5 tasks

Shorter dot syntax to access enum values #45956

FilipeBeck opened this issue Sep 19, 2021 · 17 comments
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript

Comments

@FilipeBeck
Copy link

FilipeBeck commented Sep 19, 2021

Suggestion

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Allow access to enum values without having to type the entire path to the value. Any subpath starting with dot is valid.

namespace Geometry {
  export enum Shape {
    CIRCLE,
    SQUARE
  }
}

function setShape(shape: Geometry.Shape) {
  // ...
}

setShape(.CIRCLE)
// too
setShape(.Shape.SQUARE)

With unions, we have a compiler error when the subpath is ambiguous.

namespace GeometryA {
  export enum ShapeA {
    CIRCLE = 1,
    SQUARE = 2,
  }
}

namespace GeometryB {
  export enum ShapeB {
    CIRCLE = 'circle',
    SQUARE = 'square',
  }
}

function setShape(shape: GeometryA.ShapeA | GeometryB.ShapeB) {
  // ...
}

setShape(.CIRCLE) // Error. Is `1` or `'circle'`?
setShape(.ShapeA.CIRCLE) // OK

📃 Motivating Example

Sometimes, the path is too long. I have this code in react:

<Scene sampleRate={88200} latencyHint={ATOM.Scene.LatencyCategory.PLAYBACK}>

Besides having to import ATOM, the path is too long. That way it would be better:

<Scene sampleRate={88200} latencyHint={.PLAYBACK}>

This example would emit an import of ATOM at runtime.

@MartinJohns
Copy link
Contributor

MartinJohns commented Sep 19, 2021

#40343

[x] This could be implemented without emitting different JS based on the types of the expressions

Given that this is not valid JavaScript I really don't see how this would be possible without emitting different JS. Care to share?

@FilipeBeck
Copy link
Author

FilipeBeck commented Sep 19, 2021

@MartinJohns You're right. Updated

@jcalz
Copy link
Contributor

jcalz commented Sep 19, 2021

This is also

non-ECMAScript syntax with JavaScript output

Requests of this sort tend to get declined.

Usually they'd say "if you want to see this, go to tc39 and get them to put it in JavaScript first". But you're talking about enum, the epitome of non-ECMAScript syntax with JavaScript output, so I'm interested in seeing the wording of the response here. 👀

@fatcerberus
Copy link

My suspicion is that enum is just considered legacy baggage nowadays and, if it didn’t exist and had been requested today, would be declined with the same rationale as any other request for non-ES runtime features.

However, for this issue in particular, there’s another reason it’ll almost certainly be declined: it requires type-directed emit. .Circle means something different depending on the type it’s being assigned to. TS doesn’t do type-directed emit, it’s an explicit non-goal of the project.

@FilipeBeck
Copy link
Author

FilipeBeck commented Sep 20, 2021

As enum is already a non-ECMAScript syntax, it's a shame not to be able to use such a practical and intuitive feature. Without it, its use becomes discouraging. If enum is a legacy feature, then it would be better to officially deprecate it and remove it in future releases in favor of unions.

@MartinJohns
Copy link
Contributor

If enum is a legacy feature, then it would be better to officially deprecate it and remove it in future releases in favor of unions.

This would also be against the design goals of TypeScript:

Do not cause substantial breaking changes from TypeScript 1.0.

@FilipeBeck
Copy link
Author

@MartinJohns This is not a breaking change. Any current code will compile without errors after this feature. This is a minor change. Reinforcing: We're talking about enum, which is already a feature that breaks the rule

@MartinJohns
Copy link
Contributor

Removing the enum feature would be a breaking change. Note that my statement was about the quote of yours, suggesting to remove the feature from TypeScript.

@FilipeBeck
Copy link
Author

Ah, sorry. It is true

@FilipeBeck
Copy link
Author

FilipeBeck commented Sep 21, 2021

@MartinJohns In fact, you argument in this comment makes a lot of sense: #45968 (comment)

But, as i said, enum already exists and is a exception. We can do a last improvement by adding this syntax.

@fatcerberus
Copy link

We can do a last improvement by adding this syntax.

Except we can't because it requires type-directed emit, as noted in my comment above. See Non-Goals here:
https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals#non-goals

Specifically # 5 on that list:

[Don't] Add or rely on run-time type information in programs, or emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.

@FilipeBeck
Copy link
Author

The enum itself already does this:

TS:

enum A {
  CASE1 = 'case1'
}

const a = A.CASE1

JS:

"use strict";
var A;
(function (A) {
    A["CASE1"] = "case1";
})(A || (A = {}));
const a = A.CASE1;

As const:

const enum A {
  CASE1 = 'case1'
}

const a = A.CASE1

JS:

"use strict";
const a = "case1" /* CASE1 */;

That is, it emits different code based on the results of the type system

I don't see sense in not be able to emit different code based on the results of the type system with a new syntax for a type that emits different code based on the results of the type system

@FilipeBeck
Copy link
Author

@FilipeBeck
Copy link
Author

I think this illustrates my point better:

shorter-dot-syntax-outlaw.mp4

@andrewbranch
Copy link
Member

FWIW I love this feature of Swift, but yeah, sadly it’s a no-go for us. I have thought about trying to make completions let you type something like you suggested latencyHint={.P/*cursor here*/ } and triggering completions for the whole qualified enum member from the dangling . so you could accomplish the same feel as writing Swift, but haven’t had time to investigate it.

@andrewbranch andrewbranch added Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript labels Sep 24, 2021
@tjjfvi
Copy link
Contributor

tjjfvi commented Sep 24, 2021

But, as i said, enum already exists and is a exception. We can do a last improvement by adding this syntax.

Perhaps this analogy will help:

> Feature Request: It would look really cool if we dumped some gasoline all over this building and dropped a lit match inside
< That would require setting a building on fire, which we don't do
> But the building is already on fire, so it's ok
< Well, yeah, that was before we stopped setting buildings on fire. We have to keep the flaming building because some people use it to cook their meals, but we're not setting any more buildings on fire.
> Ok, so I get that we don't set new buildings on fire, but if we flung some gasoline on the existing flaming building, it would be an even better fire
< ...

@FilipeBeck
Copy link
Author

esco-fosforo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

6 participants