ES8 Proposal: Optional Static Typing
Thanks for writing this up!
My personal feeling is that I’d rather ES not have static typing of this form. I believe it makes the language substantially more complex, and to me it feels best to try to minimize the growth of the language in order to ensure that those things that we do add are added in a compatible and sound way. Also, I wonder if static typing wouldn’t work out better if instead of extending ES, you found a way to instead make wasm (www.w3.org/community/webassembly, www.w3.org/community/webassembly) powerful enough to handle the language that you want. The benefit of that route is that (1) there is already support behind wasm and what I see as a broad desire to enable wasm to handle statically-typed object-oriented/functional languages, and (2) it’s a clean slate, so a proposal doesn’t have to worry about the compatibility issues.
Personal feelings aside, I’ve got questions/comments:
On Jul 18, 2015, at 12:35 PM, Brandon Andrews <warcraftthreeft at sbcglobal.net> wrote:
ES8 Proposal: Optional Static Typing
It was once said by Brendan Eich that 'Types are hard. This doesn't mean "no, never".' It's been a while since ES4. With ES6's TypedArrays and classes finalized and ES7 SIMD getting experimental tests, ECMAScript is in a good place to finally discuss types again. The demand for types as a different approach to code has been so strong in the past few years that separate languages have been created to deal with the perceived shortcomings. Types won't be an easy discussion, nor an easy addition, since they touch a large amount of the language; however, they are something that needs rigorous discussion. I'm hoping this initial proposal can be a way of pushing the ball forward. Turning this into an official proposal discussed by TC39 is the goal. This could very well be most of ES8 due to the complexity.
Since it would be potentially years before this would be implemented this proposal includes a new keyword "enum" for enumerated types and the following types:
number bool string object int8/16/32/64 uint8/16/32/64
I agree that these types are fundamental.
bignum float16/32/64/80/128 decimal32/64/128 int8x16/16x8/32x4/64x2/8x32/16x16/32x8/64x4 uint8x16/16x8/32x4/64x2/8x32/16x16/32x8/64x4 float32x4/64x2/32x8/64x4 rational complex
Are these types baked into the language, or can these types be user-defined? It’s fine if they are user-definable but present in the standard library, but it seems weird if these were baked in at the language level.
any void
These types bring ECMAScript in line or surpasses the type systems in most languages. For developers it cleans up a lot of the syntax, as described later, for TypedArrays, SIMD, and working with number types (floats vs signed and unsigned integers). It also allows for new language features like function overloading and a clean syntax for operator overloading. For implementors, added types offer a way to better optimize the JIT when specific types are used. For languages built on top of Javascript this allows more explicit type usage and closer matching to hardware.
In theory the following current keywords could be deprecated in the long-term: Boolean, Number, String, Object, and the TypedArray objects. Their methods and features would be rolled into the new type system.
Deprecating these would be almost impossible given how widely used they are. We still have to support JS idioms from before ES1 standardization (like function.arguments).
One of the first complications with types is typeof's behavior. All of the above types would return their string conversion including bool. (In my experience "boolean" is seen as verbose among C++ and C# developers. Breaking this part of Java's influence probably wouldn't hurt to preserve consistency for the future).
The next few parts cover type features that should be supported. The examples aren't meant to be exhaustive but rather show parts of the language that require new grammar and discussion.
Support for nullable types.
var foo:uint8? = null;
Support for resizable typed arrays.
var foo:uint8[]; foo.push(1); var bar:uint8[] = [1, 2, 3, 4];
Currently, typed arrays are not resizable. Are you suggesting that this is a separate type from the existing typed array types?
Support for fixed-length typed arrays:
var foo:uint8[4]; foo.push(0); // invalid foo.pop(); // invalid var bar:uint8[4] = [1, 2, 3, 4];
How do you propose to handle:
var foo:uint8[4] = …; var bar:uint8[8] = …; if (p) return foo; else return bar;
Is this a type error, or does the function have some type? This is an important question. Usually, non-resizable arrays do not store the length as part of the type, since preserving a relationship between length and type is generally a hard problem. The trivial approach - making the above be a type error - was done in Pascal and it was very restrictive. For example, it makes it hard to write a reusable function over non-resizable arrays, since every user will have to agree on the length.
The ability to type any variable including arrow functions.
var foo:uint8 = 0; var foo = uint8(0); // Cast
var foo:(int32, string):string; // hold a reference to a signature of this type var foo = (s:string, x:int32) => s + x; // implicit return type of string var foo = (x:uint8, y:uint8):uint16 => x + y; // explicit return type
Function signatures with constraints.
function Foo(a:int32, b:string, c:bignum[], callback:(bool, string) = (b, s = 'none') => b ? s : ''):int32 { }
Simplified binary shifts for integers:
var a:int8 = -128; a >> 1; // -64, sign extension var b:uint8 = 128; b >> 1; // 64, no sign extension as would be expected with an unsigned type
Destructing assignment casting:
[a:uint32, b:float32] = Foo();
What do you propose should happen if Foo does not return these types?
Function overloading:
function Foo(x:int32[]) { return "int32"; } function Foo(s:string[]) { return "string"; } Foo(["test"]); // “string"
Currently, a function declaration causes the creation of a variable that points to that function. What would the Foo variable point to in this case?
Constructor overloading:
// 4 byte object value class MyType { x:float32; // Able to define members outside of the constructor constructor(x:float32) { this.x = x; } constructor(y:uint32) { this.x = float32(y) * 2; } }
Similar issue here.
Number would convert implicitly with precedence given to decimal, float128/80/64/32/16, uint64/32/16/8, int64/32/16/8. (Or whichever order makes the most sense). As an example using the MyType class:
var t:MyType = 1; // float32 constructor call var t:MyType = uint32(1); // uint32 constructor called
Implicit constructors could also be added to the proposed SIMD classes to go from a scalar to a vector.
var v:float32x4 = 1; // Equivalent to an ES7 SIMD splat, so var v = float32x4(1, 1, 1, 1);
Implicit array conversion would also exist:
var t:MyType[] = [1, 2, 3, uint32(1)];
Types would function exactly like you'd expect with decorators in ES7, but with the addition that they can be overloaded:
function AlwaysReturnValue(value:uint32) { return function (target, name, descriptor) { descriptor.get = () => value; return descriptor; } } function AlwaysReturnValue(value:float32) { /* ... */ }
Class example and operator overloading:
class Vector2d { x: float32; y: float32; constructor(x:float32 = 0, y:float32 = 0) { this.x = x; this.y = y; } Length():float32 { return Math.sqrt(x * x + y * y); // uses Math.sqrt(v:float32):float32 due to input and return type } get X():float64 // return implicit cast { return this.x; } set X(x:float64) { this.x = x / 2; } operator +(v:vector2d) { return new vector2d(this.x + v.x, this.y + v.y); } operator ==(v:vector2d) { // equality check between this and v } }
Partial class in MyClass.js defining extensions to Vector2d:
class Vector2d { operator ==(v:MyClass) { // equality check between this and MyClass } operator +(v:MyClass) { return v + this; // defined in terms of the MyClass operator } }
Enumerations with enum that support any type except function signatures.
enum Count { Zero, One, Two }; // Starts at 0 var c:Count = Count.Zero;
enum Count { One = 1, Two, Three }; // Two is 2 since these are sequential var c:Count = Count.One;
enum Count:float32 { Zero, One, Two };
Custom sequential function (these aren't closures):
enum Count:float32 { Zero = (index, name) => index * 100, One, Two }; // 0, 100, 200 enum Count:string { Zero = (index, name) => name, One, Two = (index, name) => name.toLowerCase(), Three }; // "Zero", "One", "two", "three"
Index operator:
enum Count { Zero, One, Two }; Count[0];
Get enum value as string:
// Not sure what the syntax for this would be.
Generic functions:
function Foo<T>(foo:T):T { var bar:T; }
Generic classes:
class Vector2d<T> { x: T; y: T; constructor(x:T = 0, y:T = 0) // T could be inferred, but that might be asking too much. In any case T must have a constructor supporting a parameter 0 if this is a class. { this.x = x; this.y = y; } }
Generic constraints aren't defined here but would need to be. TypeScript has their extends type syntax. Being able to constrain T to an interface seems like an obvious requirement. Also being able to constrain to a list of specific types or specifically to numeric, floating point, or integer types. Another consideration is being able to support a default type. Also generic specialization for specific types that require custom definitions. There's probably more to consider, but those are the big ideas for generics.
Do you propose that generics behave as if there was type erasure under the hood, or as if every instantiation splats out a new class? If the latter, what does the expression “Vector2d” without any instantiation give? The challenge here is that your proposal in general, and generics in particular, seem like they would have trouble interoperating with old ES code that does not have types. I think the big problem to solve here is how to make generics work properly while still being optional.
Filip Pizlo wrote:
Hi Brandon,
Thanks for writing this up!
My personal feeling is that I’d rather ES not have static typing of this form. I believe it makes the language substantially more complex, and to me it feels best to try to minimize the growth of the language in order to ensure that those things that we do add are added in a compatible and sound way. Also, I wonder if static typing wouldn’t work out better if instead of extending ES, you found a way to instead make wasm (www.w3.org/community/webassembly) powerful enough to handle the language that you want. The benefit of that route is that (1) there
+1
Two things:
I heard Gilad Bracha in ECOOP to say [paraphrase] "optional typing is great to help with documentation, and to help the tools; but it should not touch the runtime". This is what I take for my own as well, nicely said. Your overload proposition, though, changes the language so that this would need to touch the runtime. It seems there are two distinct view on bringing static typing in to dynamically-typed languages: one of them in line with the aformentioned, the other taking over the semantic and runtime as well. There are also two terms: "optional typing" and "gradual typing". I really don't know if those are defined precisely, but there seemed to be a sentiment that "optional typing" is the Gilad-Brachaish Strongtalk-like view, and "gradual typing" is the view that actually changes the semantics of the language and the runtime. What I wanted to say, is, that maybe this thread should be called "ES8 Proposal: Gradual Static Typing".
And I forgot the second one. Though I personally think changing the semantics (do gradual typing) would change the language to something very different and may complicate the good old duck-typing way of doing things. Truly optional typing, in the sense of "help the tools and document" is good, though. But it is completely different debate (some foods for though: 1. We have JSDoc which already does that - we can type-annotate and tools do the work; 2. An eye-opener is to learn that there is distinction between nominal types and structural types. Does it not suit ES, as a duck-typing language, to have optional structural typing instead of optional nominal typing?).
My personal feeling is that I’d rather ES not have static typing of this form. I believe it makes the language substantially more complex, and to me it feels best to try to minimize the growth of the language in order to ensure that those things that we do add are added in a compatible and sound way. Also, I wonder if static typing wouldn’t work out better if instead of extending ES, you found a way to instead make wasm (www.w3.org/community/webassembly) powerful enough to handle the language that you want. The benefit of that route is that (1) there is already support behind wasm and what I see as a broad desire to enable wasm to handle statically-typed object-oriented/functional languages, and (2) it’s a clean slate, so a proposal doesn’t have to worry about the compatibility issues.
It's an understandable response. Just looking at the changes required is absolutely daunting in the complexity. The way I view it though is growing the language slowly, without types in place, is actually causing more complexity and limiting the language for future growth. The way TypedArrays and SIMD for instance were approached are needlessly verbose. Now that types are being seen as a requirement the way they're handled is being done poorly. The aversion to topics like operator overloading being the clear candidate when looking at SIMD. The complexity continues when looking at the syntax proposed for class operator overloading in the previous suggestions www.slideshare.net/BrendanEich/js-resp/15 . This is something that will continue as more types and features are added. Setting up a framework for types and language features early on will allow these features to be added with clean polyfills.
As much as I like the idea of WebAssembly I don't see it being viable for decades. In the mean-time ECMAScript will still exist and grow as a language. One day it'll be implemented on top of WebAssembly and personally I'd like Javascript to age well. It's rather close to being a very general purpose language. (I use it on the client, server, and database(PLV8) which is pretty awesome. It'll be years before WebAssembly gets to that level).
bignum
float16/32/64/80/128 decimal32/64/128 int8x16/16x8/32x4/64x2/8x32/16x16/32x8/64x4 uint8x16/16x8/32x4/64x2/8x32/16x16/32x8/64x4 float32x4/64x2/32x8/64x4 rational complex
Are these types baked into the language, or can these types be user-defined? It’s fine if they are user-definable but present in the standard library, but it seems weird if these were baked in at the language level.
Yes, they'd be baked into the language as core types. One of the big reasons is that they'd be natively implemented generally by implementations. There is no fast way to implement bignum without a native implementation. I've written bignum algorithms in Javascript before and never got performance anywhere close to native. Same for SIMD operations, thus the SIMD proposal in ES7.
In theory the following current keywords could be deprecated in the long-term: Boolean, Number, String, Object, and the TypedArray objects. Their methods and features would be rolled into the new type system.
Deprecating these would be almost impossible given how widely used they are. We still have to support JS idioms from before ES1 standardization (like function.arguments).
It's something that could co-exist for years along-side the preferred ones. If ES8 has an implementation a warning might exist that would continue for a decade. A lot of web developers have kind of got used to the idea of living specs from HTML5 and CSS's continuous changes. It's not that the features would be removed, they just might not exist forever or in all implementations.
Support for resizable typed arrays.
var foo:uint8[]; foo.push(1); var bar:uint8[] = [1, 2, 3, 4];
Currently, typed arrays are not resizable. Are you suggesting that this is a separate type from the existing typed array types?
Yes. A resizable typed array is a new types proposed here. What we have right now would be essentially a var foo:any[] = [1, 'foo', {}]; example. A resizable typed array, like the current type array, has finer control for allocation with the advantage of native implementation speed. I was talking to people comparing this to C++'s Vector class. If it should have things like a capacity would be up for debate if it's even worth it.
Support for fixed-length typed arrays:
var foo:uint8[4]; foo.push(0); // invalid foo.pop(); // invalid var bar:uint8[4] = [1, 2, 3, 4];
How do you propose to handle:
var foo:uint8[4] = …; var bar:uint8[8] = …; if (p) return foo; else return bar;
Is this a type error, or does the function have some type? This is an important question. Usually, non-resizable arrays do not store the length as part of the type, since preserving a relationship between length and type is generally a hard problem. The trivial approach - making the above be a type error - was done in Pascal and it was very restrictive. For example, it makes it hard to write a reusable function over non-resizable arrays, since every user will have to agree on the length.
You're exactly right about them not storing the length. I view fixed-sized arrays as a high performance use-case. If the user were to use your example I might say that the function signature would need to define either a resizable array or a fixed sized array of the maximum size. The resizable would probably be the default case essentially converted from a fixed size to a resizable array.
function Foo(p:boolean):uint8[] // default case
{
var foo:uint8[4] = ...;
var bar:uint8[8] = ...;
return p ? foo : bar;
}
function Foo(p:boolean):uint8[8] // return a resized foo
{
var foo:uint8[4] = ...;
var bar:uint8[8] = ...;
return p ? foo : bar;
}
Probably for someone concerned with performance I'd probably not recommend using such code anyway. While it does give the Javascript engine all it needs to figure out what to do optimally they'd need to actually implement it sanely.
The ability to type any variable including arrow functions.
var foo:uint8 = 0; var foo = uint8(0); // Cast var foo:(int32, string):string; // hold a reference to a signature of this type var foo = (s:string, x:int32) => s + x; // implicit return type of string var foo = (x:uint8, y:uint8):uint16 => x + y; // explicit return type Function signatures with constraints. function Foo(a:int32, b:string, c:bignum[], callback:(bool, string) = (b, s = 'none') => b ? s : ''):int32 { } Simplified binary shifts for integers: var a:int8 = -128; a >> 1; // -64, sign extension var b:uint8 = 128; b >> 1; // 64, no sign extension as would be expected with an unsigned type Destructing assignment casting: [a:uint32, b:float32] = Foo();
What do you propose should happen if Foo does not return these types?
Either an implicit cast is performed or a type error is raised. It would need to be debated on which implicit casts should exist. Personally I like implicit casts for as many things as possible where they make sense. For instance, I like string to integer implicit conversions while others would cry fowl and say parseInt(x, 10) is the only true way. There are obvious cases though where a type error is a clear winner and can be caught at run-time:
var foo:(int32):int32[] = (i) => i * 2;
foo(42); // No suitable cast exists from int32 to int32[].
Function overloading:
function Foo(x:int32[]) { return "int32"; } function Foo(s:string[]) { return "string"; } Foo(["test"]); // “string"
Currently, a function declaration causes the creation of a variable that points to that function. What would the Foo variable point to in this case?
Internally it could be a few things. An array of function signatures would work. When using the () operator it would access the closest matching signature. If someone wanted to reference specific functions there could be syntax for that, but it's not something that's usually required.
Generic functions:
function Foo<T>(foo:T):T { var bar:T; }
Generic classes:
class Vector2d<T> { x: T; y: T; constructor(x:T = 0, y:T = 0) // T could be inferred, but that might be asking too much. In any case T must have a constructor supporting a parameter 0 if this is a class. { this.x = x; this.y = y; } }
Generic constraints aren't defined here but would need to be. TypeScript has their extends type syntax. Being able to constrain T to an interface seems like an obvious requirement. Also being able to constrain to a list of specific types or specifically to numeric, floating point, or integer types. Another consideration is being able to support a default type. Also generic specialization for specific types that require custom definitions. There's probably more to consider, but those are the big ideas for generics. Do you propose that generics behave as if there was type erasure under the hood, or as if every instantiation splats out a new class? If the latter, what does the expression “Vector2d” without any instantiation give? The challenge here is that your proposal in general, and generics in particular, seem like they would have trouble interoperating with old ES code that does not have types. I think the big problem to solve here is how to make generics work properly while still being optional.
Type erasure seems fine creating a new class. I mentioned specialization. So Vector2d without any suitable generic would throw a type error unless one defined a default. Either by doing:
class Vector2d
{
x:any;
y:any;
}
or
class Vector2d<T = uint32>
{
}
(Or some other syntax).
Programmers can be odd and implement specializations differently (different members) which could complicate things also. Regarding old code, a lot of old code and dynamically typed code will essentially be interpreted as having "any" or "number" depending on the context. It shouldn't cause huge issues. I mentioned before that number would have implicit casts to the new types. You might need to wrap parameters in a cast to choose a certain overload if the wrong one is chosen implicitly.
I heard Gilad Bracha in ECOOP to say [paraphrase] "optional typing is great to help with documentation, and to help the tools; but it should not touch the runtime". This is what I take for my own as well, nicely said. Your overload proposition, though, changes the language so that this would need to touch the runtime. It seems there are two distinct view on bringing static typing in to dynamically-typed languages: one of them in line with the aformentioned, the other taking over the semantic and runtime as well. There are also two terms: "optional typing" and "gradual typing". I really don't know if those are defined precisely, but there seemed to be a sentiment that "optional typing" is the Gilad-Brachaish Strongtalk-like view, and "gradual typing" is the view that actually changes the semantics of the language and the runtime. What I wanted to say, is, that maybe this thread should be called "ES8 Proposal: Gradual Static Typing".
I'm definitely advocating run-time changes. Adding static typing that does nothing other than help with code-hinting or documentation would be pointless. I don't think anyone would seriously consider changing the language just for that. While having cleaner documentation, code-hinting, and more readable code would be useful a bigger change is performance. Giving programmers the ability to write clear code that also gives them finer control over allocation and data structures is key in the continued and expanding usage of Javascript. It's with these changes I'm hoping Javascript can be used more as a scripting language to further fill gaps where other languages are used. I think that's also the view many hold when they proposed SIMD integration. SIMD alone though is a targeted fix for a few applications. Types on a whole solve many small performance issues across numerous applications.
Since I can't edit the original post I moved everything over to a github repo. It's easier to read the proposal there.
Added bool SIMD types to the proposal and explained the operators more. Lot of edge cases to still define though, but it's looking rather solid. I've shown it to others that are used to TypeScript and they seem to agree with the general syntax. Still have to think more about generic constraints and typedefs/aliases/"interfaces" though and how they fit into Javascript moving forward.
Does anyone doing something to push this proposal forward? I’m sure that optional static typing is something that should be presented in ES. Currently we have a lot of buzz around TypeScript just because it has static typing.
I'm just an observer who occasionally contributes to this list, but I don't see static types making it into ES any time soon.
First, no type checker (TypeScript, Flow, or any other) can fully check the
core language (most notably bind
, apply
, call
, and
Object.assign
).
Second, they both still are missing some pretty significant features
required for typing common JavaScript idioms (higher kinded types for
Fantasy Land and Ramda users, variadic generics for bind
, call
, and
apply
, n-ary unions for Object.assign
and similar, etc.).
Third, I'd rather it remain just an extension to the language, maintained independently. Research on types is still very active, since it's a very young field. It's still a work in progress on how to best type otherwise dynamic languages. It may be plausible in the future to go Python's route and allow for a particular syntax without mandating it have semantic implications, but I feel even that's likely years off.
Does anyone doing something to push this proposal forward? I’m sure that optional static typing is something that should be presented in ES. Currently we have a lot of buzz around TypeScript just because it has static typing.
I'm just an observer who occasionally contributes to this list, but I don't see static types making it into ES any time soon.
First, no type checker (TypeScript, Flow, or any other) can fully check the
core language (most notably bind
, apply
, call
, and
Object.assign
).
Second, they both still are missing some pretty significant features
required for typing common JavaScript idioms (higher kinded types for
Fantasy Land and Ramda users, variadic generics for bind
, call
, and
apply
, n-ary unions for Object.assign
and similar, etc.).
Third, I'd rather it remain just an extension to the language, maintained independently. Research on types is still very active, since it's a very young field. It's still a work in progress on how to best type otherwise dynamic languages. It may be plausible in the future to go Python's route and allow for a particular syntax without mandating it have semantic implications, but I feel even that's likely years off.
ES8 Proposal: Optional Static Typing
It was once said by Brendan Eich that 'Types are hard. This doesn't mean "no, never".' It's been a while since ES4. With ES6's TypedArrays and classes finalized and ES7 SIMD getting experimental tests, ECMAScript is in a good place to finally discuss types again. The demand for types as a different approach to code has been so strong in the past few years that separate languages have been created to deal with the perceived shortcomings. Types won't be an easy discussion, nor an easy addition, since they touch a large amount of the language; however, they are something that needs rigorous discussion. I'm hoping this initial proposal can be a way of pushing the ball forward. Turning this into an official proposal discussed by TC39 is the goal. This could very well be most of ES8 due to the complexity.
Since it would be potentially years before this would be implemented this proposal includes a new keyword "enum" for enumerated types and the following types:
number bool string object int8/16/32/64 uint8/16/32/64 bignum float16/32/64/80/128 decimal32/64/128 int8x16/16x8/32x4/64x2/8x32/16x16/32x8/64x4 uint8x16/16x8/32x4/64x2/8x32/16x16/32x8/64x4 float32x4/64x2/32x8/64x4 rational complex any void
These types bring ECMAScript in line or surpasses the type systems in most languages. For developers it cleans up a lot of the syntax, as described later, for TypedArrays, SIMD, and working with number types (floats vs signed and unsigned integers). It also allows for new language features like function overloading and a clean syntax for operator overloading. For implementors, added types offer a way to better optimize the JIT when specific types are used. For languages built on top of Javascript this allows more explicit type usage and closer matching to hardware.
In theory the following current keywords could be deprecated in the long-term: Boolean, Number, String, Object, and the TypedArray objects. Their methods and features would be rolled into the new type system.
One of the first complications with types is typeof's behavior. All of the above types would return their string conversion including bool. (In my experience "boolean" is seen as verbose among C++ and C# developers. Breaking this part of Java's influence probably wouldn't hurt to preserve consistency for the future).
The next few parts cover type features that should be supported. The examples aren't meant to be exhaustive but rather show parts of the language that require new grammar and discussion.
Support for nullable types.
var foo:uint8? = null;
Support for resizable typed arrays.
var foo:uint8[]; foo.push(1); var bar:uint8[] = [1, 2, 3, 4];
Support for fixed-length typed arrays:
var foo:uint8[4]; foo.push(0); // invalid foo.pop(); // invalid var bar:uint8[4] = [1, 2, 3, 4];
The ability to type any variable including arrow functions.
var foo:uint8 = 0; var foo = uint8(0); // Cast
var foo:(int32, string):string; // hold a reference to a signature of this type var foo = (s:string, x:int32) => s + x; // implicit return type of string
var foo = (x:uint8, y:uint8):uint16 => x + y; // explicit return type
Function signatures with constraints.
function Foo(a:int32, b:string, c:bignum[], callback:(bool, string) = (b, s = 'none') => b ? s : ''):int32 { }
Simplified binary shifts for integers:
var a:int8 = -128; a >> 1; // -64, sign extension
var b:uint8 = 128; b >> 1; // 64, no sign extension as would be expected with an unsigned type
Destructing assignment casting:
[a:uint32, b:float32] = Foo();
Function overloading:
function Foo(x:int32[]) { return "int32"; } function Foo(s:string[]) { return "string"; } Foo(["test"]); // "string"
Constructor overloading:
// 4 byte object value class MyType { x:float32; // Able to define members outside of the constructor constructor(x:float32) { this.x = x; } constructor(y:uint32) { this.x = float32(y) * 2; } }
Number would convert implicitly with precedence given to decimal, float128/80/64/32/16, uint64/32/16/8, int64/32/16/8. (Or whichever order makes the most sense). As an example using the MyType class:
var t:MyType = 1; // float32 constructor call var t:MyType = uint32(1); // uint32 constructor called
Implicit constructors could also be added to the proposed SIMD classes to go from a scalar to a vector.
var v:float32x4 = 1; // Equivalent to an ES7 SIMD splat, so var v = float32x4(1, 1, 1, 1);
Implicit array conversion would also exist:
var t:MyType[] = [1, 2, 3, uint32(1)];
Types would function exactly like you'd expect with decorators in ES7, but with the addition that they can be overloaded:
function AlwaysReturnValue(value:uint32) { return function (target, name, descriptor) { descriptor.get = () => value; return descriptor; } } function AlwaysReturnValue(value:float32) { /* ... */ }
Class example and operator overloading:
class Vector2d { x: float32; y: float32; constructor(x:float32 = 0, y:float32 = 0) { this.x = x; this.y = y; } Length():float32 { return Math.sqrt(x * x + y * y); // uses Math.sqrt(v:float32):float32 due to input and return type } get X():float64 // return implicit cast { return this.x; } set X(x:float64) { this.x = x / 2; } operator +(v:vector2d) { return new vector2d(this.x + v.x, this.y + v.y); } operator ==(v:vector2d) { // equality check between this and v } }
Partial class in MyClass.js defining extensions to Vector2d:
class Vector2d { operator ==(v:MyClass) { // equality check between this and MyClass } operator +(v:MyClass) { return v + this; // defined in terms of the MyClass operator } }
Enumerations with enum that support any type except function signatures.
enum Count { Zero, One, Two }; // Starts at 0 var c:Count = Count.Zero;
enum Count { One = 1, Two, Three }; // Two is 2 since these are sequential var c:Count = Count.One;
enum Count:float32 { Zero, One, Two };
Custom sequential function (these aren't closures):
enum Count:float32 { Zero = (index, name) => index * 100, One, Two }; // 0, 100, 200
enum Count:string { Zero = (index, name) => name, One, Two = (index, name) => name.toLowerCase(), Three }; // "Zero", "One", "two", "three"
Index operator:
enum Count { Zero, One, Two }; Count[0];
Get enum value as string:
// Not sure what the syntax for this would be.
Generic functions:
function Foo<T>(foo:T):T { var bar:T; }
Generic classes:
class Vector2d<T> { x: T; y: T; constructor(x:T = 0, y:T = 0) // T could be inferred, but that might be asking too much. In any case T must have a constructor supporting a parameter 0 if this is a class. { this.x = x; this.y = y; } }
Generic constraints aren't defined here but would need to be. TypeScript has their extends type syntax. Being able to constrain T to an interface seems like an obvious requirement. Also being able to constrain to a list of specific types or specifically to numeric, floating point, or integer types. Another consideration is being able to support a default type. Also generic specialization for specific types that require custom definitions. There's probably more to consider, but those are the big ideas for generics.
Typedefs or aliases for types are a requirement. Not sure what the best syntax is for proposing these. There's a lot of ways to approach them. TypeScript has a system, but I haven't see alternatives so it's hard for me to judge if it's the best or most ideal syntax.
Undecided Topics:
I left value type classes out of this discussion since I'm still not sure how they'll be proposed. Doesn't sound like they have a strong proposal still or syntax.
Unions are another topic not covered mostly because the syntax is very subjective. Without an added keyword the following might work in the grammar. The example uses an anonymous group unioned with an array of 3 elements. Using a class with x, y, and z members would also work. (Or using an interface syntax if one is added could work).
class Vector3d { { { x: float32; y: float32; z: float32; } a: float32[3]; } }
Another example would be:
class Color { { { Red: float32; Green: float32; Blue: float32; Alpha: float32; } Vector: float32x4; } }
The last topic not covered is if there should be new syntax for TypedArray views.
var a1:uint32[] = [2, 0, 1, 3]; var a2:uint64[] = a1(1, 2); // Creates a view of a1 at offset 1 with 2 elements. So [0, 1].
That might be asking too much though. In a very compact form:
var foo = ((uint64[])a1(1, 2))[0]; // foo would be 1
This has been brought up before, but possible solutions due to compatability issues would be to introduce "use types"; or since ES6 has them Brenden once suggested something like:
This concludes my proposal on types and the many facets of the language that would be potentially touched. The goal is essentially to turn this, or something similar, into a rough draft. Essentially build a foundation to start from expanding on edge cases and changes required in each part of the language. I'm sure with enough minds looking at each section this could be very well defined by the time ES8 is being considered.
Previous discussions: This one contains a lot of my old thoughts (at least the stuff from 8 months ago). esdiscuss.org/topic/proposal-for-new-floating-point-and-integer-data-types, esdiscuss.org/topic/optional-strong-typing
esdiscuss.org/topic/optional