Typing for TypeScript Aseem Rastogi University of Maryland College Park Nikhil Swamy Cédric Fournet Gavin Bierman Panagiotis Vekris Microsoft Research ID: 276126
Download Presentation The PPT/PDF document "Safe & Efficient Gradual" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
Safe & Efficient Gradual Typingfor TypeScript
Aseem RastogiUniversity of Maryland, College ParkNikhil Swamy Cédric Fournet Gavin Bierman Panagiotis Vekris (Microsoft Research) (Oracle) (UCSD)Slide2
Gradual TypingCombines benefits of static and dynamic typing
2Statically-typed fragmentDynamically-typed fragment
Dynamic type
any
Runtime
checks mediate
interaction
Static
type errors
Performance
Typed interface documentation
Rapid prototyping
FlexibilitySlide3
TypeScript, Dart, Closure, Flow (we focus on TypeScript)
Increase programmer productivity(static type errors, intellisense, code refactoring, modularity …)Types don’t get in the way of good programmers(retain flavor of
programming in JavaScript
)
3
I
ncreasing Adoption in Industry
M
ainly for
JavaScriptSlide4
TypeScript is Intentionally UnsoundFor All Its Dynamic Idioms, Typing JavaScript is Hard !
Unsound typing rules to support supposedly common idioms(covariant array subtyping, covariant function arguments, …)
Types
are
uniformly erased
during compilation
(
lightweight compilation, no runtime performance cost, …
)
(
unchecked runtime casts, types don’t guarantee anything)4Slide5
Programmers Cannot Rely On Types5
private parseColorCode (c:string) { if (typeof c !==
"
string"
)
return
-1;
…
}
Snippet from
TouchDevelop, a large TypeScript DevelopmentTools can’t rely on typesRefactoring, compiler optimizations, etc. are not safeSlide6
6Can we design a gradual type system that is:
SafeEfficientSupports idiomatic TypeScript/JavaScriptWe present Safe TypeScriptSlide7
Safe TypeScript is Sound
TypeScript is Intentionally UnsoundStandard variance for arrays and function argumentsSound treatment of JavaScript thisCareful separation of nominal and structural types
Easy local rewriting of unsound idioms in 120,000 line corpus
TypeScript has unsound typing rules to support common idioms
7Slide8
Safe TypeScript is Sound
TypeScript is Intentionally UnsoundSafe TypeScript guarantees type safetyCombination of static checking and runtime checksRuntime-type-information (RTTI) based gradual typingEfficiency using two new notions of partial erasureRuntime overhead of only 15% for bootstrapping Safe TypeScript
6.5% for typed Octane benchmarks
TypeScript uniformly erases all types
making uses of
any
unsafe
8Slide9
9Formalized
core of Safe TypeScript, proven sound class C, interface I, {M;F}, number, any, Erased t
Implemented in a branch of TypeScript v0.9.5
Can be invoked with --safe command line flag
Evaluated on 120,000 lines of code
Bootstrapped Safe TypeScript compiler, New TypeScript compiler, Octane
O
ur ContributionsSlide10
10Safe TypeScript Tour
Overview of RTTI-based Gradual TypingValues carry Run Time Type Tagsconsistent with their contentsDynamically-typed code i
nstrumented
to
respect
RTTI
tags
Invariant: v : [|
v.tag
|]
Object with 3 fields and type tag
f = 2g = "s"h = truetag = {f:number; g:string}Slide11
interface Point { x:
number }function g(p:Point) { p.x = p.x + 1;}
function
f() {
var
p:Point = { x = 0 };
g(
p);
}11
Compiles unchangedNo runtime checksNo tagging !
Previous systems tag eagerly
RTTI Tagging in Safe TypeScript
On-demand and Differential
Safe TypeScript
JavaScriptSlide12
12
RTTI Tagging in Safe TypeScriptOn-demandfunction g(p:
any
)
{
p.x
=
p.x
+ 1;}
function f() { var p:Point = { x = 0 };
g(p);}function f() { var p = { x = 0 }; g(shallowTag(p, Point));}
shallowTag(
x,t
) =
x.tag
:= combine(
x.tag
, t);
x
Safe TypeScript
JavaScript
Add RTTI when types lose precisionSlide13
13
RTTI Tagging in Safe TypeScriptDifferentialshallowTag(x,t) = x.tag
:= combine(
x.tag
, t);
x
interface
2dPoint
extends
Point {
y:number}function
h(p:any) { .. }function g(p:Point) { h(p);}function f() { var p:2dPoint = { x = 0; y = 0 }; g(p);}function
g(p) {
h(
shallowTag
(p, Point));
}
function
f
()
{
var
p = { x = 0; y = 0 };
g(
shallowTag
(p, { y:
number
}));
}
Safe TypeScript
JavaScript
Add Minimum Required RTTISlide14
14
RTTI Tagging in Safe TypeScriptshallowTag(x,t) = x.tag := combine(x.tag
, t);
x
function
g(p) {
h(
shallowTag
(p, Point));
}
function f
() { var p = { x = 0; y = 0 }; g(shallowTag(p, { y:number }));}JavaScriptAdd Minimum Required RTTI
x = 0
y = 0Slide15
15
RTTI Tagging in Safe TypeScriptshallowTag(x,t) = x.tag := combine(x.tag
, t);
x
function
g(p) {
h(
shallowTag
(p, Point));
}
function f
() { var p = { x = 0; y = 0 }; g(shallowTag(p, { y:number }));}JavaScriptAdd Minimum Required RTTI
x = 0
y = 0
tag = {y:number}
x = 0
y = 0Slide16
16
RTTI Tagging in Safe TypeScriptshallowTag(x,t) = x.tag := combine(x.tag
, t);
x
function
g(p) {
h(
shallowTag
(p, Point));
}
function f
() { var p = { x = 0; y = 0 }; g(shallowTag(p, { y:number }));}JavaScriptAdd Minimum Required RTTI
x = 0
y = 0
tag = {x:number; y:number}
x = 0
y = 0
tag = {y:number}
x = 0
y = 0Slide17
Differential Subtypingt1
<: t2 ~> d d = 0 | td is loss in precision that must be captured in the RTTI17
D
ifferential Subtyping
Technical Device
for On-Demand and Differential TaggingSlide18
t <: t ~> 0{
x:number;y:number} <: {x:number} ~> {y:number}{x:number
;y:
number
} <:
any
~> {
x:
number
;y:number
}Primitive RTTI Aware: number <: any
~> 018Differential Subtypingt1 <: t2 ~> d d is loss in precisionSlide19
Γ |- e : t2 ~> e’t2 <: t1 ~> d
Γ |- e : t1 ~> shallowTag(e’, d) 19T-Sub
Differential Subtyping
t1
<: t2 ~> d d
is
loss
in
precisionSlide20
20
Instrumentation of Dynamically Typed Codefunction g(p:any
)
{
p.x
=
"boom"
;
}
function f() {
var p:Point = { x = 0 }; g(p); assert(typeof p.x === "number");}
function
f() { .. }
function
g(
p) {
write
(p,
"x"
,
"boom"
);
}
write(
o,f,v
) =
let t =
o.tag
;
o[f] = check(v, t[f]);
(Recall that
f
tags
p
with type
Point
)
// Fails
Safe TypeScript
JavaScriptSlide21
21Differential Tagging Useful, But Not Ideal
function g(p) { return p.x
; }
function
f(p) {
var
p = { x = 0; y = 0 };
g(
shallowTag
(p, { y:number }));}
Safe TypeScriptJavaScriptfunction g(p:Point) { return p.x;
}
Unnecessary
shallowTag
function
f() {
var
p:2dPoint = { x = 0; y = 0 };
g(p);
}Slide22
22Reason: Need Conservative Tagging
Previous gradual type systems:
t
any
for all static types
t
Incurs tagging even for statically typed code
Relaxing it opens opportunities for sound
type erasureSlide23
A new type modality: Erased
tErased t cannot be cast to anyStatically a tbut may not have an RTTI at runtime
23
Erased Types
Programmer-controlled Tagging BehaviorSlide24
t1 <: t2 ~> dt1
<: Erased t2 ~> 0Erased t <\: any24
Subtyping for Erased Types
// Zero delta
// Ensure full erasure is safe Slide25
25Erased Types Example: No Tagging
Safe TypeScriptJavaScript
function
g(p)
{
return
p.x
; }
function
f(p) { var p = { x = 0; y = 0 };
g(shallowTag(p, { y:number }));}function g(p:Point) { return p.x;}
Unnecessary
shallowTag
function
f() {
var
p:2dPoint = { x = 0; y = 0 };
g(p);
}Slide26
26Erased Types Example: No Tagging
function g(p) { return p.x; }
function
f(p) {
var
p = { x = 0; y = 0 };
g
(p)
;
}Safe TypeScript
JavaScriptfunction g(p:Erased Point) { return p.x;
}
function
f() {
var
p:2dPoint = { x = 0; y = 0 };
g(p);
}
// No tagging
Recall
shallowTag
for non-erased typesSlide27
27Erased Types Example: No Tagging
function f(p) { var p = { x = 0; y = 0 }; g(p)
;
}
Safe TypeScript
JavaScript
function
g(
p:Erased
Point
) { return h(p);}function f() { var p:2dPoint = { x = 0; y = 0 }; g(p);}
// No tagging
function
h(p:
any
) { .. }
Static Type Error Slide28
Obey a fully static type discipline(
Sound type erasure, type abstraction, …)(Loss in expressiveness: not all code can be turned dynamic)Other advantages of Erased typesWorking with external libraries that may not have RTTIAdding new features to the type system (e.g. Polymorphism)
See our paper for more details
28
Erased
TypesSlide29
29Soundness Theorem: Forward Simulation
Checks introduced by Safe TypeScript:Catch any dynamic type errorDo not alter the semantics of type safe code
Tag heap evolution invariantSlide30
Implemented in a branch of TypeScript v0.9.5
Invoke our type checker with --safe flag10,000 Lines of CodeCompiles to plain JavaScript (with instrumented checks)(recursive interfaces, inheritance, overloading, generics, arrays, external libs, …)30
Safe TypeScript Implementation
Open Source on GithubSlide31
Bootstrapping Safe TypeScript
90,000 Lines of Code, heavily class based, carefully annotatedNumber of Errors
Covariant method
arguments
130
Variable scoping
128
Potential unsound use of
this
52
Bivariant array subtyping98...…Total478 Static Type Errors found in Safe TypeScript CodeSlide32
Bootstrapping Safe TypeScript
90,000 Lines of Code, heavily class based, carefully annotated Static Type Errors found in Safe TypeScript Code
All cases in one file using visitor pattern, easily rewritten
Sloppy
use of var declarations, easily fixed
Projection of methods, easily fixed by making them functions
Fixed by introducing array mutability qualifiers
Number of Errors
Covariant method
arguments
130
Variable scoping
128
Potential unsound use of
this
52
Bivariant
array subtyping
98
...
…
Total
478Slide33
Bootstrapping Safe TypeScript
90,000 Lines of Code, heavily class based, carefully annotated478 Static type errors26 failed runtime downcasts; 5 in our own code !15% runtime overhead of type safetySlide34
Octane Benchmarks (6 / 17)
22x (ave.) and 72x (max.) overhead with no type annotations
Down to
6.5%
after adding type annotations
No overhead for statically-typed code
Found a variable scoping bug in
navier
-stokes
It has since been fixed
Efficiency conditioned on precise type
inferenceTypeScript quite conservativeSlide35
Nominal handling of prototype-based classesSound handling of thisAbstraction theorem for
Erased { }Zero-subtyping to avoid object identity issuesMore experiments(TypeScript v1.1, Octane benchmarks, Different tagging schemes)35Also in the paper …Slide36
When to use Safe TypeScript ?
For mature TypeScript projects, improved code qualityGood in development setting for finding early bugsBaseline for JS analyses focusing on deeper properties36Safe TypeScript Summary
Safe
& Efficient Gradual Typing for TypeScript
Significant value for type annotations at a modest cost
(runtime checks at
least during development and testing)
Give it a go !Slide37
37
interface Point {..}interface 2dPoint {..}
Erased Types Example: Type Abstraction
function
g(p:Point) {
write(p,
"
y
"
, 3);}function f() {..}RTTI of p at write is {y:number}
write
succeeds: abstraction violation
function
g(p:Point) {
(<
any
> p).y = 3;
}
function
f() {
var
p:2dPoint =
{x = 0;y
=
0}
;
g(p);
}
Safe TypeScript
JavaScriptSlide38
38
function g(p:Point) { (<any> p).y = 3;}
function
f() {
var
p:2dPoint =
{x = 0;y
=
0};
g(p);}interface
Point extends Erased {..}interface 2dPoint extends Point {..} Static Type Error Erased Types Example: Type Abstraction
Safe TypeScript
JavaScript