This should have been implemented eons ago. I can understand it's unavailability in class methods (You have to ask yourself why you don't just use a static property in the class itself). But it's becoming a pain to have to manually add funcname.staticvar = ... after my function definitions.
I really need this because:
HTTP RAW Message -> RequiestObject / RAW Cookie -> CookieObject)let x = ...;
function y() {
// do stuff with x
}
Thank you for your time. I really hope this can be implemented before version 2. I'm sure a lot of people will be very happy.
TypeScript Version:
1.8.0
Code
function aFncWithStaticVariable() {
static let foo = 100;
static let bar = "bar";
static private let privar = true; // Just in case
}
Expected behavior:
function aFncWithStaticVariable() {
}
aFncWithStaticVariable._foo = 100;
aFncWithStaticVariable._bar = "bar";
aFncWithStaticVariable._privar = true;
Actual behavior:
test.ts(2,2): error TS1184: Modifiers cannot appear here.
test.ts(3,2): error TS1184: Modifiers cannot appear here.
test.ts(4,2): error TS1184: Modifiers cannot appear here.
The following works currently:
function aFncWithStaticVariable() {
}
namespace aFncWithStaticVariable {
export let foo = 100;
export let bar = "bar";
let privar = true; // Just in case
}
Function-private static variables are a pattern in JavaScript that is a bit awkward to express in TypeScript. I'd certainly make use of the static syntax if it was available inside functions.
The dual function/namespace declaration is a bit unweildy and exposes the variables outside the function, whereas function-static vars are usually a private implementation detail of the function. EDIT: Also, the function is hoisted but the namespace isn't, leading to possible ReferenceErrors.
I'd like to be able to write something like this:
// Given a Foo, compute the corresponding Bar.
function expensiveComputation(foo: Foo): Bar {
// Cache common results to save re-computing them
static let lruCache = new LRUCache<Foo, Bar>();
// If the result is already cached, return it now
if (lruCache.has(foo)) return lruCache.get(foo);
// Compute the result
let bar: Bar = /*** expensive computation ***/
// Add to cache and return
lruCache.set(foo, bar);
return bar;
}
Which would downlevel to something like (sans comments):
function expensiveComputation(foo) {
expensiveComputation._lruCache = expensiveComputation._lruCache || new LRUCache();
if (expensiveComputation._lruCache.has(foo)) return expensiveComputation._lruCache.get(foo);
let bar: Bar = /*** expensive computation ***/
expensiveComputation._lruCache.set(foo, bar);
return bar;
}
Why not use the tried-and-true IIFE approach? Capturing the variable in a closure is actually quite idiomatic and has other advantages as well. Or maybe I'm misunderstanding what you're trying to do...
@aluanhaddad TypeScript uses IIFEs a lot in its downlevelled output (e.g. for namespaces and classes). So things like namespaces and classes are just syntax sugar for what can be equivalently expressed with just IIFEs.
But the syntax sugar serves a purpose - it makes the code easier to read and expresses intent more clearly. Function-static variables could just be another syntax sugar with clear intent, that downlevels to IIFEs.
@yortus indeed, but I don't think that this particular use case warrants an additional concept. It could get quite complicated. For example, if someone tries to use a static variable declaration in a method, what should be the result? Should the method be promoted to a property? Surely not. Should it be shared among instances of the class? Surely not. Should it be an error?
@aluanhaddad I would restrict this suggestion to function declarations only. As the OP points out, it doesn't make much sense on methods anyway.
Hoisting is another point against the dual function/namespace declaration approach and the IIFE approach.
With hoisting, a function may be called before it is declaration, and that works fine. But it's accompanying namespace won't have been evaluated yet, so referring to any exported variables in the namespace may generate ReferenceErrors.
IIFEs aren't hoisted so are not equivalent to function declarations containing static vars as per this suggestion.
That is indeed true, because namespaces are implemented as IIFEs. However, your example downlevel transpilation example
function expensiveComputation(foo) {
expensiveComputation._lruCache = expensiveComputation._lruCache || new LRUCache();
if (expensiveComputation._lruCache.has(foo)) return expensiveComputation._lruCache.get(foo);
let bar: Bar = /*** expensive computation ***/
expensiveComputation._lruCache.set(foo, bar);
return bar;
}
exposes the static, cached property. Also, as you implied, the approach only works with function statements, potentially named function expressions could work as well. It won't work with either => functions or anonymous function expressions. It feels overly specific.
It feels overly specific.
Perhaps for you, but I use this pattern from time to time, especially for memoizing and caching. It's useful in functional code.
your example downlevel transpilation example [...] exposes the
static, cached property
Only in the same sense that TypeScript exposes the private properties of classes. They don't show up in intellisense and it's a compile error to reference them outside their scope, but if you bypass the type system with some casts to any you can access them, yes.
Anecdotal, but a quick google search for 'javascript static variable' yields mostly pages asking/describing how to do exactly what's described in this issue. E.g. all from the first page of results:
I don't think this is an esoteric coding practice.
@yortus one could argue that this practice was highly common when the way to create a "class" in JavaScript up until the syntactic sugar provided with ES6 Classes was to create a constructor function and decorate it with "static" methods and properties and then assign it a prototype. People have been trying to twist JavaScript to match their expectations of a language for years.
one could argue that this practice was highly common when the way to create a "class" in JavaScript
I think the semantics are quite different in most cases. People coming from C++ or even PHP are quite familiar with static variables in functions. They have quite distinct uses from classes.
People have been trying to twist JavaScript to match their expectations of a language for years.
@kitsonk sure, with the addition of OO class syntax being a particularly successful example of this.
People have been trying to twist JavaScript to match their expectations of a language for years.
Quite. And it has often been to the detriment of their code quality and understanding of the language.
People coming from C++ or even PHP are quite familiar with static variables in functions.
As someone with a C++ background, I don't think TypeScript or JavaScript has any obligation to match my expectations from that language.
There is an established way to do this kind of caching over successive invocations in JavaScript, and it's one of the things the language does very elegantly.
@aluanhaddad it's unclear whether you are arguing that this specific suggestion reduces code quality, or you are generally against new syntactic constructs that have some equivalent ES3. Are you also against classes, namespaces, modules, async/await, types, and other 'twists' too? TypeScript provides two forms of productivity: type checking and downlevelling. Are you against the latter in general, or just this specific case?
If you could furnish an elegant example of a strongly-typed function declaration that caches its results in a private static variable, whilst remaining hoistable, I'd like to see it. It would advance your argument much better than just saying so, IMHO.
it's unclear whether you are arguing that this specific suggestion reduces code quality, or you are generally against new syntactic constructs that have some equivalent ES3. Are you also against classes, namespaces, modules, async/await, types, and other 'twists' too? TypeScript provides two forms of productivity: type checking and downlevelling. Are you against the latter in general, or just this specific case?
To be clear, I'm arguing against this specific case. I'm not against down leveling by any means and async/await, modules, arrow functions, and to an extent classes are all features which raise the level of abstraction in meaningful ways. They are also features which are in, or will likely be in, a future version of ECMAScript.
I suppose that given the requirement for hoistabiliy, the closest you can get today is your example compiled output
function expensiveComputation(foo) {
expensiveComputation._lruCache = expensiveComputation._lruCache || new LRUCache();
if (expensiveComputation._lruCache.has(foo)) return expensiveComputation._lruCache.get(foo);
let bar: Bar = /*** expensive computation ***/
expensiveComputation._lruCache.set(foo, bar);
return bar;
}
What I am against is arbitrary, non-orthogonal syntactic extensions that increase the complexity of the language while introducing a number of sharp edges and special cases. This would introduce a local variable declaration form that is only valid within function statements. But really, my subjective argument has nothing to do with down leveling. I just don't think this warrants downleveling, and the argument that it is in language x or language y is not particularly compelling.
the argument that it in language x or language y is not particularly compelling.
nobody has made this argument. But the OP listed three arguments that have some merit.
I think you've mentioned the best argument _against_ this suggestion, which is that it's a syntax extension that does not currently appear to be a JavaScript proposal at any stage. It would be risky to introduce it, as JavaScript might eventually get this feature but with different syntax. I guess @wotsyula you might want to propose this as a syntax extension for ESNext, and if that succeeds one day it will end up in TypeScript.
I guess I can we can all sacrifice the ability to use functions with static variables before they are defined.
foo(); // Error Function used before defenition
function foo() {
static x = 1;
}
foo(); // Okay
@aluanhaddad By forcing usage after the definition then any form of implementation becomes valid. Also you don't have to include an expression in the function itself to insure the variable is set. I can live with this. It's still better than nothing. I truly understand how difficult it is to implement this in JavaScript. Every solution seems to create 2 or more problems. I agree that the final solution relies in ES
@yortus I'm not a fan of es. I feel that the future of JavaScript is in compilation not advancement of features. What we need are better JavaScript compilers. Not a better JavaScript. Look at the C programming language. Very simple and very usefull. But if we need it we can use C# C++ and many more. JavaScript has been around since 1990s without major improvements. Just because a _small_ group of people are getting together to try to change that doesn't mean its gonna happen tomorrow.
As noted in https://github.com/Microsoft/TypeScript/issues/8419#issuecomment-216351820, the function+namespace pattern is the recommended approach for defining static variables.
Although an approach is suggested here https://github.com/Microsoft/TypeScript/issues/8419#issuecomment-216351820, it seems impossible to use it for instance methods? Correct me if I am wrong.
@ycmjason this feature wouldn't make sense for instance methods. I argued that it would be confusing since it would be special syntax only valid in function statements and maybe named function expression but neither methods nor => functions would make any sense.
TypeScript is just sad.
use var
@pixelass no u
馃檮