Typescript: suggestion to inline const keywords for ES5

Created on 21 Jul 2015  Â·  16Comments  Â·  Source: microsoft/TypeScript

it would be great to transpile const declaration with inline value when target is ES5 (as already Done with const enum), so it will prevent to include useless javascript.
An example:

const A = "myConstValue";
alert(A);
// generated js code:
alert("myConstValue" /* A */)

Another example:
module MyConstMudule
{
   export const A = "myConstValue";
}
alert(MyConstModule.A);
// generate js code:
alert("myConstValue" /* MyConstModule.A */);

what do you think?

Out of Scope Suggestion

Most helpful comment

IMHO this is a good feature, if I get it right it would be like #define in C++ or inline in Pascal.

Sometimes you need to use a value in a couple of different places but it does not worth to declare a constant for it, so the compiler should just copy the value to wherever the "inline constant" has been used.

example
inline const NUM = 1.53676758;
const result = inputVar / NUM + NUM;

should compile to...

const result = inputVar / 1.53676758 + 1.53676758;

It avoids mistakes if and when you need (for some reason) to tweak the value of the "inline constant".

All 16 comments

How does it get emitted in a .d.ts for instance? what if the const value is dependent on non-const values or has side effects, e.g.

declare function foo():number;

const x = foo();

console.log("here is x: " + x);
console.log("again: " + x);

the const enums are restricted to only constant expressions which is different from the const semantics; so we can not copy the implementation, we need to come up with a different proposal.

Suggestion: limit that scope to what enums can handle. And FWIW, in my enum proposal, I already created an algorithm to figure out most of what's safe to replace. It's intentionally limited to booleans, numbers, strings, null, and undefined, and function calls do not count. It's towards the end of this section.

As for functions themselves, there's a lot of bikeshedding to do to figure out how, if possible, to judge and/or enforce if a function is truly immutable or not.

If functions are explicitly designated as immutable, it's not hard to verify those functions as immutable. And for languages that are immutable by default, it's even easier. Inferring if a function is mutable is easy - it's a matter of "Is there any way this can cause side effects?". Merely an assignment to a reference the function didn't create is enough to say yes (variable outside a closure, assignment to member, etc.).

As for inferring immutability, that's probably impossible to do completely. Even in C/C++, as advanced as their compilers are, they still can only partially make that judgement, even with the static types and explicit reference vs value, and even without lexical closures (in the case of C). This is still a CS research topic in trying to figure how far they can. As for why it's likely impossible to fully infer this, try to factor in branch checking or recursion (which requires branch checking) into the process. It would require partially running the program to figure some parts out. You'd have to build a Turing-complete scope checker to figure that out.

This is then just constant propagation. it is not strictly tied to const declarations or enums. we will have to allow constant expressions to emitted in .d.ts files (which today they do not) to enable this feature.

@mhegazy I'm aware of that. That's basically what the suggestion would entail, just as the enum situation currently does. I am fairly neutral on this, although IMHO it would be better for the compiler to allow some sort of pluggable transform like what Babel does, or a similar framework to allow that. The API exists to do the manipulations, but there isn't much of an ecosystem to go with it.

This seems like something that could benefit styled components which use template literals for building CSS. If this change supported template literals that would be great.

For example:

const VALUE = 10;
const aString = `A ${VALUE} B`;

Could be output as (although this might break tagged template literals).

const aString = `A ` + 10 + ` B`;

Or potentially just

const aString = `A 10 B`;

@TazmanianDI This is unsafe for three scenarios:

  1. Symbols (they are not coercible)
  2. Reference types (.toString is called)
  3. Tagged template literals (as you already mentioned)

In all other use cases, this is a safe thing to do. Conveniently, constant propagation works only for literals, and all literals except objects and functions are primitives, so the only problematic construct would be with tagged template literals.

Note that this could increase code size, and so it should be disabled by default.

I might be missing the point here a little, but why not just use the same syntax with enums: const type?

This is what I'm trying to do:

// index.d.ts
export declare namespace API {
    namespace ListUsers {
        const type ENDPOINT = '/users';
        const type METHOD = 'GET';
    }
}

// api.ts
const listUsers = () => Axios.request({
    baseUrl: process.env.API_BASE_URL,
    method: API.ListUsers.METHOD,
    url: API.ListUsers.ENDPOINT
}).then(response => response.data);

I can do this just fine with const enum, but I'd not like to make single-value enums.
Let me know if this is actually a separate feature request 😄

@G-Rath Not a fan of using const type for a value...it seems incredibly off and weird. Not TS team obviously, but I don't think they'll be a fan of it either.

@isiahmeadows fair enough - I agree it's not the most ideal or best looking, but it does at least match const enum, and it's unused syntax right now...

It's a least a starting proposal :joy:

@G-Rath I do like the idea of having a means to tell the compiler it's safe to fully inline certain constants. That might be a way to get around the unsafe cases.

IMHO this is a good feature, if I get it right it would be like #define in C++ or inline in Pascal.

Sometimes you need to use a value in a couple of different places but it does not worth to declare a constant for it, so the compiler should just copy the value to wherever the "inline constant" has been used.

example
inline const NUM = 1.53676758;
const result = inputVar / NUM + NUM;

should compile to...

const result = inputVar / 1.53676758 + 1.53676758;

It avoids mistakes if and when you need (for some reason) to tweak the value of the "inline constant".

This is a pretty old ticket so the current template might not have been in place, but doesn't it violate this requirement from the current template:

  • [ ] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)

@dragomirtitian Not entirely - constant inlining only affects emit, not runtime semantics (unless you care about Function.prototype.toString behavior). Also, that general philosophy was itself held to some extent when the issue was first created, so it's been through quite a bit of discussion already. They aren't entirely against runtime features in theory, just the runtime side is complete enough that there's virtually nothing left to add that's genuinely useful and 99.9999% of valid feature requests are just about implementing various ES proposals. So although that checkbox does have a reason, there are a few limited scenarios (like this) where you can justify changes to runtime and/or emit behavior.

A new declaration syntax for which Typescript would enforce having a constant expression as a RHS and that would be inlined should be added.

Something like:

const const MAX_ROW_COUNT = 5;
// or 
inline MAX_ROW_COUNT = 5;

const const is nice because it doesn't create a new keyword, but inline is less verbose
inline const is also an interesting option that could allow future inlining of other things.

Currently we have to do this because exported const name can't be minified.

export const enum MAX {
    ROW_COUNT = 5
}

It is really silly.

@feeddageek wrote:

Something like:

const const 

I was thinking about that me too — then I thought it would be confusing to people learning Typescript.

I like this:

inline const STH = ...
inline enum EnumName = { ...  }

Is inline enum more welcoming & self explanatory, to people learning Typescript, than const enum?

What if having inline be a keyword only in the variable / stuff declarations part of the program? But still allowing variables and functions to have the name "inline"? for backw compatibility. (So that let inline = true would still work)

At the same time, the following is a somewhat ok workaround?

const enum CONSTANT = { _: the-value }

// And use like so:
doTheThings(CONSTANT._)

This violates the "does the same thing as JS" principle pretty hard and is getting close to type-directed emit, so I think given the feedback so far and the risk of the feature, it's safe to say this isn't happening.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

quantuminformation picture quantuminformation  Â·  273Comments

RyanCavanaugh picture RyanCavanaugh  Â·  205Comments

disshishkov picture disshishkov  Â·  224Comments

blakeembrey picture blakeembrey  Â·  171Comments

OliverJAsh picture OliverJAsh  Â·  242Comments