Concrete Types for TypeScript

TypeScript extends JavaScript with optional type annotations that are, by design, unsound and, that the TypeScript compiler discards as it emits code. This design point preserves programming idioms developers are familiar with, and allows them to leave their legacy code unchanged, while oﬀering a measure of static error checking in parts of the program that have type annotations. We present an alternative design for TypeScript , one where it is possible to support the same degree of dynamism, but where types can be strengthened to provide hard guarantees. We report on an implementation, called StrongScript , that improves runtime performance of typed programs when run on a modiﬁed version of the V8 JavaScript engine


Introduction
Perhaps surprisingly, a number of modern computer programming languages have been designed with intentionally unsound type systems.Unsoundness arises for pragmatic reasons, for instance, Java has a covariant array subtype rule designed to allow for a single polymorphic sort() implementation.More recently, industrial extensions to dynamic languages, such as Hack, Dart and TypeScript, have featured optional type systems [5] geared to accommodate dynamic programming idioms and preserve the behavior of legacy code.Type annotations are second class citizens intended to provide machine-checked documentation, and only slightly reduce the testing burden.Unsoundness, here, means that a variable annotated with some type T may, at runtime, hold a value of a type that is not a subtype of T due to unchecked casts, covariant subtyping, and untyped code.Implementations deal with this by ignoring annotations, emitting code where all types are erased, and reverting to a fully dynamic implementation.For example, TypeScript translates classes to JavaScript code without casts or checks.Unsurprisingly, the generated code neither enjoys performance benefits nor strong safety guarantees.
A gradual type system [21,18] presents a safer alternative, as values that cross between typed and untyped parts of a program are tracked and a mechanism for assigning blame eases the debugging effort by pinpointing the origin of any offending value.But the added safety comes with a runtime overhead, a price tag that, for object-oriented programs, can be steep.Also, gradual types affect the semantics of programs; adding type annotations that are more restrictive than strictly necessary can cause runtime errors in otherwise correct programs.
We argue that programmers should be given the means to express how much type checking they want to take place in any particular part of their program.Depending on their choice, they should either be able to rely on the fact that type annotations will not introduce errors in well-tested and widely deployed dynamic code, or, if they select more stringent checks, they should have guarantees of the absence of type errors and improved performance.
This paper illustrates this idea with the design of a new type system for the Type-Script language.TypeScript is an extension to JavaScript from Microsoft that introduces classes, structural subtyping, and type annotations on properties, arguments and return types.Syntactically, our extension, which we call StrongScript, is minimal, it consists of a single type constructor for concrete types (written !). 1 Semantically the changes are more subtle.Our type system allows developers to choose between writing untyped code (i.e., all variables are of type any as in JavaScript), optionally typed code that does not affect the semantics of dynamic programs (i.e., no new dynamic errors), and concretely typed code that provides the traditional correctness guarantees but affects the semantics of dynamic code (i.e., types are retained by the compiler and used to optimize the program, new dynamic errors may show up).More specifically, the goals that guided design of StrongScript are: All JavaScript programs must be valid StrongScript programs and common programming idioms should be typeable.Optional types guarantee that variables are used consistently with their declarations; concrete types are sound up to down casts.Type information should improve performance in the context of a highly-optimizing virtual machine.Support checked casts which are central to many object oriented idioms.
One of the more subtle departures between our proposal and TypeScript is a switch to nominal subtyping for classes.The reasons for this change are pragmatic: generating efficient property access code for structural subtyping is not a solved problem, whereas it is well understood for nominal subtyping.Moreover, with nominal subtyping, we can reuse the existing JavaScript subtype test.Interfaces retain their structural subtyping rules and are erased at compile-time like in TypeScript.This yields a type system where any class name C can be used as an optional type, written C, or as a concrete type, written !C.While the former have a TypeScript-like semantics, variables typed with concrete types are guaranteed to refer to an object of class C, a class that extends C, or null.We exploit concrete type annotations and nominal subtyping to provide fast property access and efficient checked casts.Unannotated variables default to type any, ensuring that JavaScript programs are valid StrongScript programs.
The contribution of this paper are twofold: Design and implementation.We implemented StrongScript as an extension to the Type-Script compiler.All the TypeScript programs we have tried run without changes on our implementation.To get a measure of performance benefits we extended Google's V8 to provide fast access to properties through concretely typed variables and floating point math with no runtime checks.Preliminary results on a small number of benchmarks show speed ups up to 22%.We also provide some evidence that the type system is not overly restrictive, as it validates all the benchmarks from [16].
Formalization.We propose trace preservation as a key property for the evolution of programs from untyped to typed.Informally adding a type annotation to a program is trace-preserving if the program's behavior is unaffected.We prove a trace preservation theorem for optional types: if expression e is untyped, and e only differs by the addition of optional types, then e and e evaluate to the same value.We do this within a core calculus, in the style of λ JS [13], that captures the semantics of the two kinds of class types.A safety theorem states that terms can only get stuck when evaluating a cast or when accessing a property from a any or optionally typed variable.We also show that our design support program evolution by proving a strengthening theorem: when a fully optionally typed program is annotated with concrete types, the program will be trace preserving.
The design of StrongScript is based on our previous work on Thorn where optional types were called like types [2,24].As with the formalization of Bierman et al. [1], we restrict our extensions to the features of TypeScript 0.9.1 [15], the last version before the addition of generics.Our implementation is based on TypeScript 1.4, but newer features are unmodified beyond assuring that they remain safe with respect to concrete types.

Motivating Example
We illustrate gradual typing, and give a brief preview of StrongScript, with an example adapted from the raytrace benchmark of Section 7. The program includes a Camera, an extract of which is in Figure 2-C1.The camera is a client of the library class, Vector, shown in Figure 1-L1.For this example, assume the classes are developed and maintained independently.

Concrete Types for TypeScript
As is often the case in dynamic languages, the library and client start out untyped (Figure 1-L1 and Figure 2-C1).This leaves the software open to modifications, something that can be beneficial when frequent change is anticipated, but also means that all operations are dynamic.Dynamic operations can fail and, if the virtual machine is unable to optimize them, are more costly.To communicate intent and provide machine-checked documentation, the library designer may annotate fields and arguments with optional types (Figure 1-L2).Fields x, y and z are expected to hold numeric values and are annotated with the generic number type.The argument to dot() is expected to be another Vector, this is also recorded with an optional type.
Optional types impose no constraints on clients.Thus, an untyped camera (Figure 2-C1) can be used with a typed vector.The benefit of ascribing an optional type to a variable is that within the variable's scope, the compiler can detect misuse.For example, k.x is erroneous because k has optional type number and numbers do not have an x property.Optional types can also be added to the camera (Figure 2-C2) with similar benefits.
As optional types lack guarantees, developers may strengthen the invariants of their code by adding concrete types (Figure 1-L3).When fields are typed as concrete numbers, written x:!number, programmers (and the compiler) can rely on the fact that a variable like x will only ever refer an instance of numeric type or null.Return types of methods can typically be made concrete without affecting clients.Making arguments concrete, on the other hand, may require some changes.The call to Math.sqrt() triggers a compilation error because the standard library is optionally typed, as all of its methods are easily replaced in Java-Script and thus can't necessarily be trusted.We can either add cast to !number, written <!number>, or use the common JavaScript idiom of adding a unary plus operator to ensure that the result is indeed a number.
The last typing step involves adding concrete types to arguments of the methods of the Vector class (Figure 1-L4).Doing so introduces a compile error in the client (Figure 2-C2) as argument k in the call v.time(k) is typed as an optional number whereas the method definition expects !number.The client must ensure that the argument is concrete (Figure 2-C3).One last point, to improve performance, StrongScript support hints like !floatNumber that give information about the expected storage format.
One important features of our design is that untyped classes can always interact with typed ones.The proper type checks is inserted by the implementation.Any combination of library and client is valid.The only exception is that C2 will not compile with L4, this because C2 is typed but its types do not agree with L4.

Background and Related Work
The divide between static and dynamic types has fascinated academics and practitioners for years.Academics come determined to "cure the dynamic" as the absence of types is viewed as a flaw.Practitioners, on the other hand, seek to supplement their testing practices with machine-checked documentation and some ahead-of-time error checking.Dynamic languages are appreciated by practitioners for their support of exploratory programming, as any grammatically correct dynamic program, even a partial program or one with obvious errors, can be run, their productivity and their smaller learning curve.Decades of research were devoted to attempts to add static types to dynamic languages.In the 1980's, type inference and soft typing were proposed for Smalltalk and Scheme [20,3,7].Inference based approaches turned out to be brittle as they required non-local analysis and were eventually abandoned.
Twenty years ago, while working at Animorphic on the virtual machine that would eventually become HotSpot, Bracha designed the first optional type system [6].Subsequent work fleshed out the design [4] and detailed the philosophy behind optional types [5].An optional type system is one that: (1) has no effect on the language's runtime semantics, and (2) does not mandate type annotations in the syntax.Strongtalk like Facebook's Hack, Google's Dart, and Microsoft's TypeScript was an industrial effort.In each case, a dynamic language is equipped with a static type system that is flexible enough to support backwards compatibility with untyped code.While optional types have benefits, they provide no guarantee of absence of type errors nor information that could be relied upon by an optimizing compiler.
Another important line of research is due to Felleisen and his collaborators.After investigating soft typing approaches for Scheme, Findler and Felleisen turned their attention to software contracts [9].In [10], they proposed wrappers to enforce contracts for higher-order functions; these wrappers, higher-order functions themselves, were in charge of validating pre-and post-conditions and assigning blame in case of contract violations.Together with Flatt, they turned higher-order contracts into semantics casts [11].A semantics cast consists of an argument (a value), a target type, and blame information.It evaluates to an object of the target type that delegates all behavior to its argument, and produces meaningful error messages in case the value fails to behave in a type appropriate manner.In 2006, Tobin-Hochstadt and Felleisen proposed a type system for, Typed Racket, a variant of Scheme that used higher-order contracts to enforce types at module boundaries [21].Typed Racket has a robust implementation and is being used on large bodies of code [22].The drawback of this approach is that contracts impose a runtime overhead which can be substantial in some programs.
In parallel with the development of Typed Racket, Siek and Taha defined gradual typing to refer to languages where type annotations can be added incrementally to untyped code [18,19].Like in Typed Racket, wrappers are used to enforce types but instead of focusing on module boundaries, any part of a program can be written in a mixture of typed and untyped code.The type system uses two relations, a subtyping relation and a consistency relation for assignment.Their work led to a flurry of research on issues such as bounding the space requirements for wrappers and how to precisely account for blame.In an imperative language their approach suffers from an obvious drawback: wrappers do not preserve object identity.One can thus observe the same object through a wrapper and through a direct reference at different types.Solutions are not appealing, either every property read must be checked or fairly severe restrictions must be imposed on writes.In a Python implementation, called Reticulated Python, both solutions cause slowdowns that are larger than 2x [23].Another  Without annotations the program evaluates to 42.When type annotations are taken into account it stops at the read of b.A type violation is reported as the required type for b is String while b holds an Int.Similar problems occur when developers put contracts that unnecessarily strong without understanding the range of types that can flow through a function.

Concrete Types for TypeScript
The Thorn programming language was an attempt to combine optional types (called like types) with concrete types [2].The type system was formalized along with a proof that wrappers can be compiled away [24].Preliminary performance results suggested that concrete types could yield performance improvements when compared to a naive implementation of the language, but it was not demonstrated that the results hold for an optimizing compiler.Our work on StrongScript started as a straightforward port of the ideas to a different context, most of the differences are due to the details of TypeScript and JavaScript.
SafeTypeScript [16] is a recent effort from Microsoft to modify TypeScript by making it safe: in a nutshell, all types are concrete, and type checks are inserted when dynamic values are cast to concrete types.This technique yields a safe language which allows dynamic types, but lacks optional types.Because type checks are always inserted, SafeTypeScript is not trace-preserving and it lacks support for evolving programs from typed to untyped.On the other hand, SafeTypeScript focused on ensuring safety within the browser which is not a goal of our work.
Figure 3 compares the main approaches to adding types to dynamic languages.Type-Script chose to preserve the semantics of untyped code at the cost of guarantees and potential performance improvements.Typed Racket has an elegant type system that provides very strong guarantees of correctness.But the semantics of untyped code may be disrupted by too strong annotations and performance pathologies can cause serious slowdowns.Reticulated Python holds the promise of reducing the performance costs of gradual typing but does not deal with trace preservation.Lastly, StrongScript lacks some of the strong guarantees of Typed Racket (in particular about blame), but provides the means for programmers to write sound typed code and makes it easy for a compiler to generate code that is predictably efficient.

4
TypeScript: Unsound by design Bierman et al. captured the key aspects of the design of TypeScript in [1].TypeScript is a superset of JavaScript, with syntax for declaring classes, interfaces, and modules, and for optionally annotating variables, expressions and functions with types.Types are fully erased: errors not identified at compile-time will not be caught at runtime.The type system is structural rather than nominal, which causes some complications for subtyping.Type inference is performed to reduce the number of annotations.Some deliberate design decisions are the source of type holes, these include: unchecked casts, <String>obj is allowed if the type of obj is supertype of String, yet no check will be done at runtime; indexing with computed strings, obj[a+b] cannot be type checked as the value of string index is not known ahead of time; covariance of properties/arguments, this is similar to the Java array subtyping rule except that TypeScript does not have runtime checks for stores.We will look more closely at the parts of the design that are relevant to our work, starting with subtyping.Consider the following well-typed program: interface P { x : number ; } interface T { y : number ; } interface Pt extends P { y : number ; dist ( p : Pt ) ; } Interfaces include properties and methods.Extend declarations amongst interfaces are not required for other purposes than documenting intent, thus Pt is a subtype of both P and T. Classes can be defined as usual, and the extends clause there has a semantic meaning as it specifies inheritance of properties.Both classes are subtypes of the interfaces declared above.Note that the dist method is overridden covariantly at argument p and that CPoint.dist in fact does require p to be an instance of CPoint.The first assignment implicitly casts Point to Pt which is allowed by structural subtyping.The function pdist will invoke dist at static types Point, yet it is invoked with a CPoint as first argument.The compiler allows the call, at runtime the access the p.color property will return the undefined value.Any type can be converted implicitly to any, and any method can be invoked on an any reference.More surprisingly, an any reference can be passed to all argument positions and be converted implicitly to any other type.
var q : any = new CPoint ( " Red " ,1 ,1) ; var d = q .dist ( o ) ; var b = o .dist ( q ) ; This last example demonstrates a case of unchecked cast.Here o is declared of type Pt and we cast it to its subtype CPoint.The access will fail at runtime as variable o refers to an instance of Point which does not have color.The compiler does not emit a warning in any of the cases above.
function getc ( x : CPoint ) { return x .color }; getc ( < CPoint > o ) ; Bierman et al. showed that the type system can be formalized as the combination of a sound calculus extended with unsound rules.For our purposes, the sound calculus is a system with records, equi-recursive types and structural subtyping.The resulting assignment compatibility relation can be defined coinductively using well-studied techniques [12].We underline the critical choice of defining any as the supertype of all the types; since upcasts are welltyped, values of arbitrary types can be assigned to a variable of type any without the need of explicit casts.Type holes are introduced in three steps.First, a rule allows downcasts to subtypes.The second step is more interesting, as it changes the subtyping relation by stating that all types are supertypes of any.This implies arbitrary values can flow into typed variables without explicit casts.No syntactic construct identifies the boundaries between the dynamic and typed world.Thirdly, covariant overloading of class/interface members and methods is allowed.
Type inference is orthogonal to our proposal.As for generics, Bierman et al. describe decidability of subtyping as "challenging" [1]; we do not consider them here, and our implementation simply inserts runtime checks to assert their type safety.Lastly, we do not discuss TypeScript's liberal use of indexing.Our implementation supports it by explicitly inserting type casts (see Section 5).

5
StrongScript: Sound when needed To make good on the promise of concrete types, StrongScript has a sound type system.This forces some changes to TypeScript's overly permissive type rules and to the underlying implementation.The runtime thus distinguishes between dynamic objects, created with the JavaScript syntax { x:v .. }, and objects which are instances of a class, created with the new C(v..) Java-like syntax.Casts are explicit and in many cases they require checks at runtime.Covariant subtyping, such as the array subtype rule, is checked at runtime as well.Moreover, class subtyping is nominal to ensure that the memory layout of parent classes is a prefix of child classes and thus that code to access properties is fast.Compared to TypeScript, subtyping is slightly simpler as we do not allow for any to be both the top and bottom of the type lattice.By design, any JavaScript program is a well-type StrongScript program, furthermore most TypeScript programs are also valid StrongScript programs -only in the rare cases discussed in Sec.5.2 are TypeScript programs rejected by our type system (see also the evaluation in Sec. 7.

2).
In what follows we introduce the main aspects of programming in our system.Code snippets should be read in sequence.

Programming with Concrete Types
We aim to let developers incrementally add types to their code, hardening parts that they feel need to be, while having the freedom to leave other parts dynamic.This is possible thanks to the interplay between the dynamic code, the flexible semantics of optionally typed variables, and the runtime guarantees of the concretely typed code.Consider the following program: As function f has been type checked assuming that its argument is a Point, we known it's body will manipulate the argument as a Point.However, whenever an object which is an instance of a class is passed to an optionally or dynamically typed context, it protects its own abstractions at runtime.In the example, p has type any, and u points to a dynamic object with a method dist typed any → any.However, u has been typed as TypedPoint; the runtime will ensure that the method dist respects the TypedPoint.distsignature any → !number and will dynamically check that the returned value is an instance of class number.As a consequence, fact(u.dist(p)) is well-typed (the concretely typed function fact is guaranteed to receive a value of type !number) and the programmer, by specifying just one optional type, can invoke the concretely-typed function fact with a value that has been computed from the dynamic world.The ability to have fine grained control over typing guarantees is one of the main benefits of StrongScript.

From TypeScript to StrongScript Types
A significant departure of our work is that we adopt nominal subtyping for classes and retain structural subtyping for interfaces and object literals.If a class C extends D, their concrete types are subtypes, denoted !C <: !D. Furthermore each concrete type is a subtype of the corresponding optional type, !C <: C, with an order on optional types that mirrors the one on concrete types: !C <: !D implies C <: D. any is an isolate with no super or subtype.Subtyping for interfaces follows [1] with the exception that an interface cannot extend a class.
Casts play a central role in the type system.Statically casts are always allowed to and from any, while casts to optional and concrete types are only permitted if one type is subtype of the other.At runtime, all programmer-inserted casts are checked, and additional casts are added by the implementation.Whenever a function with concretely typed arguments is injected in a dynamic context, the runtime adds a wrapper that uses casts to check the actual arguments.For instance, casting fact to any results in the following wrapper: To keep the syntax of the two languages in sync, several TypeScript dynamic features are rewritten as implicit casts.In particular, at function arguments and the right hand side of the assignment operator, casts to or from any and optional types are inserted automatically.For instance, the expression on the left is transformed into the one on the right: If casts from any or optional types to concrete types are inserted, they are checked exactly like explicit casts.In addition, to support TypeScript's unsafe covariant subtyping, covariant overloading is implemented by injecting casts.Finally, casts are inserted in function calls to assure that if the function is called from an untyped context, its type annotations are honored.For instance, the class CPoint below extends Point and requires a concrete type for the argument of dist: Departing from TypeScript, the type of this is not any, but the concrete type of the surrounding class.This allows calls to methods of this to be statically type checked.But it creates an incompatibility with TypeScript code which uses "method stripping".It is possible to remove a method from the context of its object, and by using the builtin function call, to call the method with a different value for this.Consider, for instance, the following example: class Animal { constructor ( public nm : string ) {}} class Loud extends Animal { constructor ( nm : string , public snd : string ) { super ( nm ) } speak () { alert ( this .nm + " says " + this .snd ) }} var a = new Animal ( " Snake " ) ; var l = new Loud ( " Chris " , " yo " ) ; var m = l .speak ; m .call ( a ) ; The speak method will be called with this referring to an Animal.This is plainly incorrect, but allowed, and will result in the string "Chris says undefined".In StrongScript, this is concrete and the stripped method will include checks that cause the call to fail.TypeScript's generic and union types are supported, but are not meaningfully checkable, and therefore may not be made concrete.Generics may reference concrete types and unions may include concrete types, however.For instance, the type Array<!number> is supported, but the type !Array<number> is not.Like other dynamic features, implicit casts are written to assure type safety at runtime.
TypeScript's enumeration types are treated as semantically identical to numbers.

Backwards compatibility
JavaScript allows a range of highly dynamic features.StrongScript does not prevent any of these features from being used, but, since their type behavior is so unpredictable, it does not attempt to provide informative types for them.For instance, as objects are maps of string

E C O O P ' 1 5
field names to values, it is possible to access members using computed strings.Thus x[y] accesses a member of x named by the string value of y, coercing it to a string if necessary; the type of the expression is always any.Assignment to x[y] may fail, if the member has a concrete type and the assigned value is not a subtype.Similarly, eval takes any string and executes it as code.StrongScript treats that code as JavaScript, not StrongScript.This is not an issue in practice as eval's uses are mostly mundane [17].The type of eval(x) is any.
Objects in JavaScript can be extended by adding new fields, and fields may be removed.An object's StrongScript type must be correct insofar as all fields and methods supported by its declared type must be present, but fields and methods not present in its type are unconstrained.As such, StrongScript protects its own fields from deletion or update to values of incorrect types, but does not prevent addition or deletion of new fields and methods.It is even possible to dynamically add new methods to classes, by updating an object prototype.None of this affects the soundness of the type system, and access to one of these in a value not typed any will result in a static type error.

Discussion
While one of previous our prototypes for StrongScript did implement blame tracking mode similar to Typed Racket, we decided to remove the feature as it did incur serious performance overheads.Wrappers require, amongst other things, specialized field access code.In Typed Racket the overheads are tolerable because the granularity of typing is coarser; wrappers are added when untyped values cross the boundary of a typed module.In our case, any method call is potentially a boundary.Fixing these performance issues is ongoing research.Our vision of blame tracking is as an optional command line switch like assertion checking to be used during debugging.
The change to nominal subtyping is controversial but practical experience suggests that structural subtyping is rather brittle. 2 In large systems, developed by different teams, the structural subtype relations are implicit and thus any small change in one part of the system could break the structural subtyping expected by another part of the system.We believe that having structural subtyping for optionally typed interface is an appropriate compromise.It should also be noted that Strongtalk started structural and switched to nominal [4].
StrongScript departs from Thorn inasmuch Thorn performs an optimized check on method invocation on optionally typed objects: rather than fully type checking the actual arguments against the method interface, it relied on the fact that this check had already been performed statically and simply compared the interface of the method invoked against the interface declared in the like type annotation.Thorn's type system is sound, but the simpler check introduces an asymmetry between optional and dynamic types at runtime which Thiemann exploited to prove that Thorn is not trace-preserving (personal communication).

Formal properties
We formalize StrongScript as an extension of the core language λ JS of [13]; in particular we equip λ JS with a nominal class-based type system à la Featherweight Java [14] and optional types.This treatment departs from Bierman et al. [1] in that they focused on typing interfaces and ignored classes, whereas we ignore interfaces and focus on classes.Thus our calculus does not include rules for structural subtyping of interface types; these rules would, assumedly, follow [1] but would add too much baggage to the formalization that is not directly relevant to our proposal.We also do not model method overloading (as discussed, StrongScript keeps covariant overloading sound by inserting appropriate casts) and references; our design enforces the runtime abstractions even in presence of aliasing.this is used to choose the correct dispatcher or getter when executing method calls and field accesses, and to identify the cases where the property name must be converted into a string.These annotations can be added via a simple elaboration pass on the core language performed by the type checker.

Runtime abstractions.
Two worlds coexist at runtime: fully dynamic objects, characterized by the any type tag, and instances of classes, characterized by the corresponding class name type tag.Dynamic objects can grow and shrink, with fields being added and removed at runtime, and additionally values of arbitrary types can be stored in any field, exactly as in JavaScript.The reduction rules confirm that on objects tagged any it is indeed possible to create and delete fields, and accessing or updating a field always succeeds. [SObject] [TUndefined] Γ undefined : undefined [TFunc] x 1 :t 1 .., Γ e : t Γ func(x 1 :t 1 ..){return e : t} : x 1 :t 1 .. e : t m(x 1 :t 1 ..){return e : t} In our design, objects which are instances of classes benefit from static typing guarantees; for instance, runtime type checking of arguments on method invocation is not needed as the type of the arguments has already been checked statically.For this, we protect the class abstraction: all fields and methods specified in the class interface must always be defined and point to values of the expected type.To understand how this is done, it is instructive to follow the life of a class-based object.The ENew rule implements the class pattern [8] commonly used to express inheritance in JavaScript.This creates an object with properly initialized fields -the type of the initialization values was checked statically by the TNew rule -and the methods stored in an object reachable via the "__proto__" field -the conformance of the method bodies with their interfaces is checked when typechecking classes, where, for class C extends D{s 1 :t 1 .. s k :t k ; md 1 .. md n }, we define: rules TClass and TMethod.For each method m defined in the interface, a corresponding function is stored in the prototype.The following type rules for method invocation can thus be derived from the rules for reading a field and applying a function: where v = func (x:!Num){ return x + 1:!Num}.In a dynamic context, method invocation initially typechecks the arguments against the parameter type annotations of the method: The expression above dynamically checks that the method argument argument is a !Num (last ECast reduction) via the cast introduced by the combination of EGetAny and ECastFun rule.Observe that the choice of the rule EGetAny was guided by the tag any of the field access.The return value is injected back into the dynamic world via a cast to any, thus matching the corresponding static type rule.Contrast this with an invocation at the optional type D for some class D that defines a method m with type !Num → t: In this case rule EGetOpt, selected via the D tag, inserts a cast to !Num → t that not only typechecks the actual arguments (as the caller can still an arbitrary object), but also casts the return value to the type t expected by the context.
Invariants of class-based objects are also enforced via the rule EDeleteNotFound, that turns deleting a field appearing in the interface of a class-based object into a no-op (which in static contexts is also forbidden by the TDelete rule), and rule EUpdate, that ensures that a field appearing in a class interface can only be updated if the type of the new value is compatible with the interface.For this, the auxiliary function tag(v) returns the type tag of an object, and is undefined on functions.
A quick inspection of the type rules shows that optionally-typed expressions -that is, expressions whose static type is C -are treated by the static semantics as objects of type !C, thus performing local type checking.At runtime, the reduction semantics highlights instead that optionally-typed objects are treated as dynamic objects except for return values.This ensures the third key property of optional types, namely that whenever field access or method invocation succeeds, the returned value is of the expected type and not any.We have seen how this is realized on method invocation; similarly for field accesses, let C be defined as class C{"f ":!Num} and compare the typing judgments {.. | t} any ["f "] : any and {.. | t} C ["f "] : !Num.Field access on an object in a dynamic context invariably returns a value of type any.Instead if the object is accessed as C, then the rule TGet states that the type of the field access is !Num (which is then enforced at runtime by the cast inserted around the return value by rule EGetOpt).
Formalization.Once the runtime invariants are understood, the static and dynamic semantics is unsurprising.As usual, in the typing judgment for expressions, denoted Γ e : t, the environment Γ records the types of the free variables accessed by e. Object is a distinguished class name and is also the root of the class hierarchy; for each class name C we have a distinguished class name C proto used to tag the prototype of class-based objects at runtime.Function types are covariant on the return type, contravariant on the argument types: since the formalization does not support method overriding, it is sound for the this argument to be contravariant rather that invariant, which simplifies the presentation; the implementation supports overriding and imposes invariance of the this argument.Optional types are covariant and it is always safe to consider a variable of type !C as a variable of type C. The type rule for an object simply extracts its type tag, which, as discussed, is any for dynamic JavaScript objects, 4 and a class name for objects generated as instances of classes (possibly with the proto suffix).The notation C[s] returns the type of field s in class definition C; it is undefined if s does not belong to the interface of C. Auxiliary functions fields(C) and methods(C) return the set of all the fields and methods defined in class C (and superclasses).The condition not_function_type(C[s]) ensures that method updates in class-based objects are badly typed.Evaluation contexts are defined as follows: As mentioned above, method invocation has higher priority than field access, and reduction under contexts (rule ECtx) should try to reduce e t [e ](e 1 ) to v t [v ](v 1 ) whenever possible.

Metatheory.
In StrongScript, values are functions, and objects whose fields contain values.We say that an expression is stuck if it is not a value and no reduction rule applies; stuck expressions capture the state of computation just before a runtime error.The Safety theorem states that a well-typed expression can get stuck only on a downcast (as in Java) or on an optional-typed or dynamic expression.This theorem relies on two lemmas, the Preservation lemma states that typings (but not types) are preserved across reductions, and the Progress lemma identifies the cases above as the states in which well-typed terms can be stuck.The Safety theorem has several interesting consequences.First, a program in which all type annotations are concrete types has no runtime errors (apart from those occurring on downcasts): the concretely typed subset of StrongScript behaves as Featherweight Java (and, in turn, Java) and execution can be optimized along the same lines.Second, optional-typed programs (that is, programs with no occurrences of the any type and no downcasts to like types), benefit from the same execution guarantee: static type checking is strong enough to prevent runtime errors on entirely optional-typed programs.
The Trace Preservation theorem captures instead the idea that given a dynamic program, it is possible to add optional type annotations without breaking its runtime behavior; more precisely, if the type checker does not complain about the optional type annotation, then the runtime guarantees that the program will have the same behavior of the unannotated version.This theorem holds trivially in TypeScript because of type erasure.

Theorem 2 (Trace Preservation)
. Let e be an expression where all type annotations are any and Γ e : any.Let v be a value such that e −→ * v. Let e be e in which some type annotations have been replaced by optional type annotations (e.g.C, for C a class with no concrete types in its interface).If Γ e : t for some t, then e −→ * v.
The Strengthening theorem states that if optional type annotations are used extensively, then the type checking performed is analogous to the type checking that would be performed by a strong type system à la Java.A consequence is that it is possible to transform a fully optionally typed program into a concretely typed program with the same behavior just by strengthening the type annotations.This property crucially relies on the fact that all source of unsoundness in our system are identified with explicit cast to optional types (or to any).

Theorem 3 (Strengthening)
. Let e be a well-typed cast-free expression where all type annotations are of the form C or !C. Suppose that e reduces to the value v. Let e be the expression obtained by rewriting all occurrences of optional types C into the corresponding concrete types !C.The expression e is well-typed and reduces to the same value v.

Assignability
The type system we formalized is picky about compatibility of types at binding.For instance, it rejects a program that passes a concretely typed object into a dynamic context as: let (x:any = new Vector(1, 2, 3)) x any ["times"](4) obtained by combining the concretely typed library of Figure 1-L4 with the untyped client of Figure 2-C1.Yet this program is correct.Our implementation tries to be more user-friendly.The StrongScript typechecker, part of the compiler described in the next section, inserts implicit type-casts, allowing some code to be accepted statically which would otherwise be rejected.We say that type t 1 is assignable to type t 2 if any of the following holds: t 1 is a subtype of t 2 ; t 1 or t 2 are any; t 1 is number and t 2 is floatNumber (and concrete cases thereof); t 1 is number or !number and t 2 is an enumeration type.The last two cases are not relevant for the calculus but are supported by the implementation described in Section 7. The typechecker verifies the assignability relation on assignments, argument bindings, and return values (and, although not covered by the formalization, on comparisons (==, <, >, ...) and switch statements).If assignability does not hold, then the typechecker emits an error.Otherwise, in all the cases but the first, a cast to t 2 is inserted.
The above example implies an assignability check between !Vector and any, which holds because of the second case; a cast to any is inserted before the assignment: let (x:any = any new Vector(1, 2, 3)) x any ["times"](4) the resulting code is then checked following to the rules of Figure 4 (it is well-typed) and executed following Figure 5. Observe that assignability also enables interoperability between typed libraries and untyped clients.In the case below: it is an implicit cast to !Vector which is inserted: let (x:!Vector = !Vector any {..}) x !Vector ["times"](4) It is notable that this is the only one case of assignability implies a runtime type check: when t 1 is any and t 2 is a concrete type.Therefore assignability will not incur runtime checks in the absence of any-typed values.Assignability is also checked on type-casts, to emit warnings for "impossible" casts.

Evaluating StrongScript
Our implementation consists of two components: an extended version of the TypeScript 1.4 compiler and a JavaScript engine extended from Google's V8 engine. 56The compiler outputs portable JavaScript, so the resulting code can run on any stock virtual machine, but no performance improvement should be expected in that case.The compiler is extended with the following type related features: (a) support for concrete types and dynamic contracts at explicit downcasts, (b) checked downcasts where TypeScript does so implicitly and unsoundly (including covariant subtyping), and (c) function code suitable for both typed and untyped invocation (including dynamic contracts at untyped invocation).The compiler optionally emits intrinsics that inform the runtime of monomorphic property accesses and known primitive types: we extended the V8 runtime to understand and exploit these intrinsics to perform check-free property access in concrete types and floating-point math with no runtime checks.It should be noted that our extensions to the V8 runtime are not exhaustive, they are meant to demonstrate the potential of type information.For instance, we do not attempt to unbox integer values, only floating point numbers.A richer implementation could get even better performance by generating optimized code for all TypeScript data types.

Implementation
Supporting concrete types simply requires adding the type constructor (!) and typing rules: !C <: C and !C <: !D implies C <: D. Since we use nominal typing for classes, optional and concrete types are compatible in both optional and concrete contexts; it is thus possible to implement type checks, using JavaScript's builtin instanceof mechanism.Nominal types are retained at runtime.The compiler ensures that concrete types are always used soundly.
For this we include a small (200-line) library functions necessary to implement sound type checking.These functions rely on ECMAScript 5 features to protect themselves from being replaced or accidentally circumvented.To ensure soundness the compiler inserts dynamic contracts wherever unsafe downcasts occur, whether explicit or implicit.This is accomplished by the $$check function, which asserts that a value is of a specified type.For instance: var untyped : any = new A () ; var typed :!A = <!A > untyped ; is compiled into: var untyped = new A () ; var typed = A .$$check ( untyped ) ; The check function is simple and generic, and does not require a per-class checker.For compatibility with TypeScript, several forms of unsafe, implicit casts are allowed in the source program.Specifically, implicit unsafe casts are inserted when a value is of type any and is in the context of a function argument or the right-hand-side of an assignment expression.For instance, the following code: var unsafe :! B = <any > new A () ; implies this additional cast: which in turn generates the following JavaScript code: The cast to !B fails at runtime if B is not a supertype of A. Were this code to be rewritten with unsafe:B rather than !B, the inserted cast to B would imply no check, and the code would succeed at runtime.If the cast to any were omitted, this example would be rejected by the type checker.Covariant overloading is implemented as unsafe downcasting, as described in Sec.5.2.We describe some aspects of our type system as automatically-generated downcasts where TypeScript describes them as type compatibility.This is a matter of descriptive clarity and does not affect compatibility.Most semantically valid TypeScript 0.9.1 programs, and programs valid in TypeScript 1.0 and greater which use class types nominally and do not use features introduced after our version was forked from TypeScript, are semantically valid StrongScript with no syntactic changes.Because some literals have concrete types (e.g.0 has type !number), it is in some cases necessary to add explicit type annotations where implicit type annotations would choose inconsistent types (e.g.number in some cases and !number in others).
Efficient and sound implementation of function code.Functions with type annotations may be called from typed or untyped contexts.If they have only optional types or any, this requires no checks.However, methods of classes don not fit that description, as the this argument is always concretely typed.One option would be to check all concretely typed arguments at runtime, but this would entail unnecessary dynamic checks when types of arguments are known.Our implementation generates both an unchecked and a checked function.The checked function simply verifies its arguments and then calls the unchecked function.Calls are redirected appropriately by a compilation step.For instance, the following code: Intrinsics.With concrete types, it is possible to lay out objects at compile time, and to access fields and methods by their statically-known location in the object layout, obviating the need for hash table lookups.JavaScript, however, provides no way to explicitly specify the layout of objects.Therefore, to take advantage of known concrete objects, JavaScript code generated by StrongScript may optionally include calls to several intrinsic operations which specify types and access fields by explicit offset within objects.On supporting implementations, these intrinsics are used to drive optimizations and eliminate guards.The intrinsics are UnsafeAssumeMono, UnsafeAssumeFloat and ToFloat, which support direct reading and writing to offsets within an object, check-free assertion of float values and forced coercion of numbers to IEEE floating-point values, respectively.Objects built using UnsafeAssumeMono to write fields may be accessed by UnsafeAssumeMono, which modifies the behavior of the location cache: The location of the value is looked up by name in the first access, but following accesses cache the same location and do not perform runtime checks.Values created with ToFloat may be accessed with UnsafeAssumeFloat.This simply informs the JIT that the value will always be an IEEE floating-point value and does not require runtime type checks.Because JavaScript does not distinguish between floating-point numbers and integers, the type !floatNumber is supported, which is semantically identical to a number but hints the runtime that it should be stored as an IEEE float.For instance, the following StrongScript code:

Evaluating Performance
We measure the performance of our implementation to demonstrate that adding type information to dynamic code can yield performance benefits.For this experiment, we modified a small number of programs to give them concrete types and compared the result of running those on the V8 optimizing virtual machine against an untyped baseline.V8 is a highly optimizing, type-specializing compiler.Many of its optimizations are redundant with our intrinsics and we expect the relative speedups to reflect this fact.
As a baseline we used the benchmark suite provided by SafeTypeScript [16], which is in turn based on Octane7 .We focused on programs which use classes, as our optimizations and type system rely on their presence.The benchmarks are crypto, navier-stokes, raytrace, richards and splay.These benchmarks were changed only by the addition of concrete types.
For each benchmark, a type erased and a typed form were compiled, called the "Type-Script" and "StrongScript" forms.Each benchmark times long-running iterative processes; several thousand iterations are performed before timing begins to allow the JIT a warmup period.We compare the runtime between the two forms on the same engine.i.e., the only change is the inclusion of intrinsics and type protection.Each benchmark involves a particular benchmark function looped 1000 times.We ran each of these benchmarks 10 times, interleaving executions of each benchmark and each form to reduce timing effects.We report the values in milliseconds each run.We report the arithmetic mean and standard deviations of the results in each form, as well as the speedup or slowdown.Benchmarks were run an 8-core 64-bit AMD FX(tm)-8320 with 16GB of RAM, running Ubuntu Linux.Our modification of V8 is based on a snapshot dated April 6, 2015.The SafeTypeScript benchmarks were compared against a snapshot of SafeTypeScript also dated April 6, 2015.
Results. Figure 6 shows that no benchmarks demonstrated slowdown outside of noise.Three of the benchmarks had speed up large enough to be statistically significant.The performance benefits come from type-specialization intrinsics and direct access to fields in class instances.crypto uses primarily integer math, which does not presently benefit from our intrinsics, but also uses classes and displays a small but statistically significant speedup.navier-stokes 8 and raytrace use extensive floating point math as well as classes, and display benefits from both. Figure 6 also indicates the number of expressions, properties, and method arguments that had type annotations attached, which ranges from 18 to 116, and the standard deviations of both sets.Threats to validity.The number of programs available and their nature makes it difficult to generalize form our results.At least they point to the potential for performance improvements with concrete types.Also, it is worthy of note that conventional wisdom amongst virtual machine designers is that type annotations are not needed to get performance for JavaScript.Our result suggest that this may not be the case.Because our intrinsics are unchecked JavaScript, it is possible to use them to circumvent security properties of the engine.Although this problem would be resolved by implementing StrongScript directly rather than through a translation layer, the performance characteristics of such a system may vary somewhat from what is achieved with a JavaScript system.Similar changes would be expected if StrongScript's specialized functions (e.g.$$check and $$unchecked) were made secure from malicious code.Our measured benchmark code has no unsafe downcasts, and thus no runtime type checking.The overall benefit of our intrinsics depends on the underlying engine, and specifically the precision of its speculation.Our intrinsics would be expected to show narrower advantages over an engine with better object layout speculation; however our intrinsics ensure predictable benefits, while layout speculation relies on complex heuristics that might be invalidated with program evolution.

Conclusion
StrongScript is a natural evolution of TypeScript.Optional type annotations have proven to be useful in practice despite their lack of runtime guarantees or performance benefits.With a modicum effort from the programmer, StrongScript can provide stronger runtime guarantees and predictable performance while allowing idiomatic JavaScript code and flexible program evolution.The type systems of TypeScript and StrongScript are fundamentally different, the former being intrinsically unsound for the stated goal of typing as many JavaScript 8 navier-stokes displayed highly variable runtime in our initial configuration, with or without intrinsics, with time taken by a component of the V8 runtime beyond our control.We eliminated the warmup period for this benchmark to prevent this interference.Note that our intrinsics apply optimizations only to hot code, so this change does not benefit our runtime.
programs as possible, and the latter being sound to ensure stronger invariants when needed.
In practice, we have found that StrongScript type system does not limit expressiveness as our compiler silently inserts all the needed casts to optional types or any needed to mimic the unsound behaviors of TypeScript.The only incompatibilities between the two are due to structural vs. nominal subtyping on optional class types.However all programs well-typed in versions of TypeScript up-to 0.9.1 -which relied on nominal subtyping -are well-typed StrongScript programs, and the large benchmarks of [16] suggest that this is not a problem in practice.Compared to SafeTypeScript, our design delivers the flexibility offered by the optional types and the predictable performance given by intrinsics.In particular, in our design, optional types are not only useful for program evolution but can also durably play the role of interfaces between the dynamic and concretely typed parts of a program, avoiding the need for extra casts to concrete types.The fact that we are able to achieve performance gains on a highly optimizing virtual machine gives one more reason for developers to adopt concrete types.

Figure 2
Figure 2 Gradual insertion of types in Camera.

Figure 4
Figure 4The type system.

Theorem 1 (
Safety).Given a well-typed program Γ e : t, if e −→ * e and e is stuck, then either e = E[ !C v ] and Γ v : t with t <: !C, or e = E[{.. | t} t [v]] and t = any or t = C, or e = E[ t → t v ] and Γ v : any and v is not a function, or e = undefined.
Optional and gradual type systems.This table's first line indicates possible values of variable declared of class C.This type is either any or W to denote the possibility of encountering a wrapper.The second line shows the possible value of variable declared !C in StrongScript, they are guaranteed to be unwrapped subtypes of that class.Trace preservation holds in TypeScript, in StrongScript developers can choose to forgo this property in exchange for stronger guarantees.The last line refers to the ability of a compiler to generate fast path code for property accesses.drawback of gradual type systems is that they are not trace preserving.Consider: Concrete types, denoted by !C, represent objects that are instance of the homonymous class or its subclasses.Static type checking is performed on these, and no dynamic checks are needed in the absence of downcasts.Optional types have the same intent as TypeScript type annotations: they capture some type errors and enable features such as IDE name completion without reducing flexibility of dynamic programs.Concrete types behave exactly how programmers steeped in statically typed languages would expect.They restrict the values that can be bound to a variable and unlike other gradual type systems they do not support the notion of wrapped values or blame.No runtime error can arise from using a concretely typed variable and the compiler can rely on type informations to emit efficient code with optimizations such as unboxing and inlining.
StrongScript builds on and extends TypeScript.Syntactically, the only addition is a new type constructor, written !.This yields three three kinds of type annotations: Dynamic types, denoted by any, represent values manipulated with no static restrictions.Any object can be referenced by a any variable, all operations are allowed and any may fail at runtime.Optional types, denoted by class names C, enable local type checking.All manipulations of optionally typed variables are verified statically against C's interface.Optionally typed variables can reference arbitrary values, and so runtime checks are required to verify that those values conform to C's interface.
Some flexibility is lost by this class but the compiler can exploit the type informations to compute property offsets, remove runtime type checks and unbox values.Observe that dynamic, optional and concrete types can be mixed seamlessly; above for instance we have left the argument of the dist function dynamically typed, so that it is correct to invoke it with an arbitrary object as in t.dist({x=1;y=2}).Our strategy for program evolution is to first add optional types, catching and fixing unexpected local type errors; the programmer can then identify the parts of the code that obey to a stricter type discipline, and replace optional types with concrete types.Optional types act as a bridge to move values into the concrete world: Consider a new class definition, where the x and y fields have been strengthened as !number and as such can only refer to instances of class number:

class
CPoint extends Point { constructor ( public color : string , x : number , y : number ) {...} dist ( p :! CPoint ) { ... p .color ... }}The overloading of dist is unsound, as CPoint is a subtype of Point.It is rewritten to perform a cast, and thus a check, on its argument p: class CPoint extends Point { ... dist ( pa ) { var p :! CPoint = <!CPoint > pa ; ... p .color ...}} . Class names are ranged over by C, D, the associated optional types are denoted by C and concrete types by !C, and the dynamic type is any.The function type t 1 .. t n → t denotes explicitly typed functions while the type undefined is the type of the value undefined.The syntax of the language makes it easy to disambiguates class names from optional type annotations.
t ::= !C | C | any | t 1 .. t n → t | undefined A program consists of a collection of class definitions plus an expression to be evaluated.A class definition class C extends D{s 1 :t 1 .. s k :t k ; md 1 .. md n } introduces a class named C with superclass D. The class has fields f 1 ..f k of types t 1 ..t k and methods md 1 ..md n , where each method is defined by its name m, its signature, and the expression e it evaluates, denoted m(x 1 :t 1 .. x k :t k ){ return e:t}.Type annotations appearing in fields and method definitions in a class definition cannot contain undefined or function types.Rather than baking base types into the calculus, we assume that there is a class String and a conversion function toString; string constants are ranged over by s.Expressions are inherited from λ JS with some modifications (we often denote lists l 1 ..l n simply as l 1 ..): e ::= x | { s 1 :e 1 .. | t } | e 1 t [e 2 ] | e 1 t [e 2 ] = e 3 | delete e 1 [e 2 ] | t e | new C ( e 1 .. ) | let (x:t = e 1 ) e 2 | func(x 1 :t 1 ..){return e : t} | e(e 1 ..)Functions and let bindings are explicitly typed, expressions can be casted to arbitrary types, and the new C ( e 1 .. ) expression creates a new instance of class C.More interestingly, objects, denoted { s 1 :e 1 .. | t }, in addition to the fields' values, carry a type tag t: this is any for usual dynamic JavaScript objects, while for objects created by instantiating a class it is the name of the class.This tag enables preserving the class-based object abstraction at runtime.Additionally, field access (and, in turn, method invocation) and field update are annotated with the static type t of the callee e 1 : e 1 : t 1 .. Γ e n : t n Γ e t [s](e 1 .. eThe static view of the object controls the amount of type checking that must be performed at runtime.For this, field lookup e t [e ] is tagged at runtime with the static type t of e, as enforced by rules TGet and TGetAny.The absence of implicit subsumption to any guarantees that the tag is correct.Suppose that the class Num implements integers and defines the method + : !Num → !Num.Let class C be:class C{m(x:!Num){ return x + 1:!Num}}Invoking m in a statically typed context directly passes the arguments to the method body:3 n ) : t Γ e : any Γ e : t Γ e 1 : t 1 .. Γ e n : t n Γ e any [e ](e 1 .. e n ) : any

StrongScript Benchmark Annot. runtime std dev runtime std dev Speedup
Performance comparison on the V8 VM.Times are in milliseconds, lower is better.