Sylvain Pollet-Villard
DevRel at Worldline
@SylvainPV
hello FOSDEM !
Here are two commonly accepted definitions:
1. an explicit binding between a variable and a type that
guarantees this type always describe correctly the referenced data
variable
type
2. no lack of type safety due to looser typing rules,
for example implicit type coercion
Sounds like oil and water
image credits: Kolja Wilcke
var hi = "Hello !"
hi = 83110
hi = ["H","e","l","l",0]
hi = () => alert("Hello !")
JavaScript being dynamically typed, variables can change types during program execution which makes
types very unpredictable
Optional static type-checking for JavaScript
and a few other features: interfaces, decorators...
10 years of constant exponential growth
problem solved ?
API exploration
Docs inside IDE
Autocomplete
Code generation
Type Inference
Compile-time
optimizations
Easy Refactoring
Typo Detection
Running TypeScript compiler is now considered a loss of time
Unpredictable stuff in front-end web dev:
(unpredictable = known at run-time)
Back-end & 3rd party server responses
Browser API
(& bugs & quirks)
Client-side stored data
User inputs
Hey TypeScript,
how do I type-check all of this, please ?
Sorry, not a compiler problem,
more an applicative problem
You'll have to deal with it by yourself
if(!isValid(data)){ /* try to fix stuff */ }
☠
window.onerror = pretendWeCareAboutUnexpectedErrors
try { } catch(error){ alert(`Something bad happened: ${error}`) }
"the ability to bind an explicit type to a variable and guarantee that this type always describe correctly the referenced data."
What if we could do this dynamically at runtime ?
variable
type
Strong Dynamically Typed Object Modeling for JavaScript
class User {
constructor(name, age){
if(typeof name !== "string")
throw new TypeError("Invalid type for name")
if(typeof age !== "number")
throw new TypeError("Invalid type for age")
this.name = name
this.age = age
}
}
const joe = new User("Joe", 13);
joe.age = "twelve"
// does not throw any error
class User {
constructor(name, age){
const proxy = new Proxy(this, {
set(obj, key, value){
if(key === "name" && typeof value !== "string")
throw new TypeError("Invalid type for name")
if(key === "age" && typeof value !== "number")
throw new TypeError("Invalid type for age")
return Reflect.set(obj, key, value)
}
})
proxy.name = name
proxy.age = age
return proxy
}
}
const joe = new User("Joe", 13);
joe.age = "twelve"
// TypeError: Invalid type for age
function typecheck(obj, definition){
return new Proxy(this, {
set(obj, key, value){
if(key in definition && typeof value !== definition[key])
throw new TypeError("Invalid type for " + key)
return Reflect.set(obj, key, value)
}
})
}
class User {
constructor(){
return typecheck(this, {
name: "string",
age: "number"
})
}
}
const joe = new User();
joe.age = "twelve"
// TypeError: Invalid type for age
What's the difference ?
const Shirt = new ObjectModel({
// valid values: 38, 42, "S", "M", "L", "XL", "XXL"...
size: [Number, "M", /^X{0,2}[SL]$/]
})
const Integer = Model(Number).assert(Number.isInteger)
const PositiveInteger = Integer.extend().assert(
function isPositive(n){ return n >= 0 }
)
Shirt.errorCollector = (err) => logError(err) && cancelOrder()
Require browser support for ES6 Proxies:
(Edge 14+, Firefox 47+, Chrome 50+, Safari 10+, Node 6.0+)
USE BOTH
My recommendation:
Static anotations
in core logic
Dynamic modeling
on external interfaces
they address different issues
thanks !
Sylvain Pollet-Villard
@SylvainPV