TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

Enable constants as computed values for string enums

Open kataik opened this issue 4 years ago • 44 comments

Search Terms

'TS18033', 'typescript define enum with constant string value', 'string enum computed constant'

Suggestion

Please consider enabling to use constant variables as values for string enums. The following code snippet fails compilation with TS18033 ('Only numeric enums can have computed members, but this expression has type 'string'. If you do not need exhaustiveness checks, consider using an object literal instead.')

const VALUE: string = 'ENUM_VALUE';

enum MyEnum {
  KEY = VALUE, // <-- Fails with TS18033
}

console.log(MyEnum.KEY);

, whereas the following passes:

enum Values {
  VALUE = 'ENUM_VALUE',
}

enum MyEnum {
  KEY = Values.VALUE,
}

console.info(MyEnum.KEY);

Use Cases

Use string interpolation (templated strings) when defining the value of a string enum to be able to reuse pre-defined constants as part of the value. (E.g. namespace prefixes, extension postfixes)

Examples

const NAMESPACE: string = 'com.mycompany.myservice';

enum Errors {
  INVALID_INPUT_ERROR = `${NAMESPACE}.errors#InvalidInput`,
}

Checklist

My suggestion meets these guidelines:

  • [ 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, etc.)
  • [ X ] This feature would agree with the rest of TypeScript's Design Goals.

kataik avatar Sep 27 '20 04:09 kataik

This would be a perfectly valid feature to implement since computed number values are already allowed.

Is there any reason why computed string values shouldn't be allowed for enums? Perhaps I'm not aware of something tied to the bigger picture?

@RyanCavanaugh can anybody from the TS team comment on my assumptions above? Thanks a lot.

Inlesco avatar Oct 11 '20 07:10 Inlesco

I'm also curious about why computed enums are disabled for strings. Being able to use templated strings would be great. Looks like there was a similar issue here https://github.com/Microsoft/TypeScript/issues/20440.

moneydance avatar Nov 12 '20 20:11 moneydance

This would be handy when when writing a React component with a prop that controls a css class. Right now I'm doing the following

enum ComponentVariant {
  Foo,
  Bar,
}

const classes = {
  [ComponentVariant.Foo]: styles.fooClassName,
  [ComponentVariant.Bar]: styles.barClassName,
}

export default function TheComponent({ variant }: {variant: ComponentVariant}) {
  return <div className={classes[variant]} />
}

But it would be both cleaner, and also safer (i.e. prevent accidentally indexing into a non-existing value in the classes object) to do this directly:

enum ComponentVariant {
  Foo = styles.fooClassName,
  Bar = styles.barClassName,
}

export default function TheComponent({ variant }: {variant: ComponentVariant}) {
  return <div className={variant} />
}

fenduru avatar Apr 16 '21 00:04 fenduru

Following the namespace reasoning, highly useful for developing browser extensions or other scripts where we don't want to collision with pre-existing scripts, such as postMessage(<this value>, '*')

tomasdev avatar May 01 '21 21:05 tomasdev

I also need this

ahnpnl avatar Jul 06 '21 18:07 ahnpnl

Additionally symbols should be allowed to be used as enum values.

enum MyType {
    foo = Symbol("foo"), // Error: ts(18033) - Only numeric enums can have computed members, but this expression has type 'symbol'. If you do not need exhaustiveness checks, consider using an object literal instead.
}

RebeccaStevens avatar Nov 19 '21 07:11 RebeccaStevens

I'm with the same problem :/

Thorugoh avatar Nov 19 '21 17:11 Thorugoh

please enable, thank you!

webia1 avatar Nov 29 '21 10:11 webia1

Facing the same issue. I would love to have computed values for strings so I can assign react components to enums.

daltonrussell avatar Dec 20 '21 20:12 daltonrussell

PUSH!! i am currently facing this issue too. have an ENUM with endpoints and want to reuse pre-defined parts of that

export const customerEndpoint = '/customers';

export enum ENDPOINTS {
  INVOICES = `${customers}/invoices`,
  BASKET = `${customers}/basket`,
}

as an example

SheppardX avatar Jan 10 '22 13:01 SheppardX

+1

zheoreh avatar Jan 19 '22 21:01 zheoreh

I would love to have this on TS, thanks!

estefafdez avatar Feb 14 '22 13:02 estefafdez

This would be a great feature, currently running into this same issue

JordanDC2 avatar Mar 09 '22 21:03 JordanDC2

I'm currently working to build a enum and interfaces in order to organize my database fields, finally as a custom orm system, and all my database fields have a prefix slot_, that I wanted to specify into string literals in my enum:

// current enum
enum MyTableFields {
  OBJECT_ID = 'slot_objectID',
  CODE = 'slot_code',
  LABEL = 'slot_label'
  // ...and many other fields
}

// wanted enum
const slot_ = 'slot_'
enum MyTableFields {
  OBJECT_ID = `${slot_}objectID`,
  CODE = `${slot_}code`,
  LABEL = `${slot_}label`
}

Apart from that this little formality, I wanted to explore another idea like handling schema or database table names inside enum, like this:

// current enum
enum MyTableFields {
  OBJECT_ID = 'database_name.schema.slot_objectID',
  CODE = 'database_name.schema.slot_code',
  LABEL = 'database_name.schema.slot_label'
  // ...and many other fields
}

// wanted enum
const path = `${databaseName}.${schema}.slot_`
enum MyTableFields {
  OBJECT_ID = `${path}objectID`,
  CODE = `${path}code`,
  LABEL = `${path}label`
}

Enum with computed values could be helpfully helpful !

WilliamChazot avatar Mar 11 '22 15:03 WilliamChazot

I too want to compute two enums with slight variations, such as a prefix.

MrEmanuel avatar Apr 06 '22 14:04 MrEmanuel

+1 here, would be great innovation

stormBMO avatar Apr 14 '22 08:04 stormBMO

+1 from me as well, I would like to assign values to enum variables depending on the environment via the ternary operator.

azad-derakhshani-GS avatar Apr 25 '22 12:04 azad-derakhshani-GS

I support this as well, any prospects for implementation horizon?

iulia-codes avatar Apr 26 '22 17:04 iulia-codes

+1, this would be quite helpful

Labernator avatar Apr 27 '22 13:04 Labernator

There's another error message you might get if you try this: Computed values are not permitted in an enum with string valued members. ts(2553)

nkappler avatar Apr 27 '22 13:04 nkappler

I'm in favor if this change too. I would like to be able to do something like this:

image

aghArdeshir avatar May 19 '22 07:05 aghArdeshir

This would be ideal! Especially when declaring API route constants.

import { getExact } from "../../util/env";

enum ApiRoutes {
    users = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    blockUser = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    reverifyUser = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    resetPassword = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    preferences = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    pacemakers = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    matchPreferences = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    userLocation = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    tokens = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    messageThread = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    readMessage = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    suggestedSports = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    suggestedGenders = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    findMatch = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    likeUser = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    dislikeUser = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    createP2pReferralCode = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    p2pReferral = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    citiesAPI = 'https://wft-geo-db.p.rapidapi.com/v1/geo/cities',
    discover = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
    eventAttendance = `${getExact('REACT_NATIVE_BACKEND_URL')}/****`,
};

export default apiRoutes;

ZEAL-MATCH-LTD avatar May 19 '22 10:05 ZEAL-MATCH-LTD

I also need this feature The deletion of this feature causes my code to be difficult to maintain

GO-DIE avatar Jun 20 '22 06:06 GO-DIE

+1

embedify avatar Jun 28 '22 06:06 embedify

With all due respect for the work that has been done for TypeScript so far. (Forgive me; English is not my mother tong)

I don't know if anyone gets the opposite impression somehow, but we TS lovers are not here for trolling. On the contrary, I personally would be willing to help for free.

Unfortunately, I personally have the impression that the ts-dev-team does what they want while ignoring the community's wishes and taking well-intentioned criticism and suggestions for improvement as an insult. Please be aware; that we are the community.

For example, this issue has been waiting for a solution almost for two years, and this is not an isolated case.

Although I want to help and I always try to be polite; (because I wish TS to continue and go in the right direction because I love using TS and I've put a lot of effort into it for years), I'm being terrorized and insulted by some core devs including dev-lead. I don't know how many I've had to block lately because of this. But that was not the case 2 years ago.

I do not know if it is because of the corona, but we must establish respectful communication again. (If someone can't contribute anything positive, at least he shouldn't hinder.)

TS-Team has to take community wishes seriously. They do what they want to do, block new ideas as off-topics, close bugs without solving them, and they want us to do what they want instead of doing what we need to do.

Sorry, I won't play this game for long. I convinced a giant company (German Railways) to use TS in the middleware, which was a three-digit million project. I probably wouldn't have done that if I had been a troll.

Dear TS team, please take this seriously.

Many thanks

webia1 avatar Jul 04 '22 10:07 webia1

+1

miguelci avatar Jul 13 '22 10:07 miguelci

Meeting Notes https://github.com/microsoft/TypeScript/issues/49927 about this issue.

a-tarasyuk avatar Jul 16 '22 06:07 a-tarasyuk

+1 also useful for enums utilizing i18n translations

export enum FooBarBas {
  FOO = i18next.t('Foo'),
  BAR = i18next.t('Bar'),
  BAS = i18next.t('Bas'),
}

lyssareba avatar Aug 02 '22 17:08 lyssareba

+1

hieucd04 avatar Aug 11 '22 11:08 hieucd04

+1

bfoong-equinix avatar Aug 29 '22 07:08 bfoong-equinix