SlideShare una empresa de Scribd logo
1 de 127
Descargar para leer sin conexión
T Y P E S
F O R
F R O N T E N D
D E V E LO P E R S
In mid-2016, my team and I were a few years in to an Angular 1 project, and despite our best efforts it was starting to get a little unruly. Sure we had
guidelines we followed, high test coverage, a linter making sure we stayed in the lines, but we never really had confidence in what our code was doing.
Despite our best efforts, our code wasn’t terribly clear and our tests were riddled with boilerplate. It was next to impossible to take a break from some part
of the code, come back and understand, in a reasonable amount of time, what the code was doing.
Luckily, at that time we were going to start a new project and we had complete freedom to figure out the stack. We looked at new frameworks, different
languages all the while keeping in mind that whatever we chose needed to help us make our code more understandable and easier to test.
To that end, we started looking at
T Y P E S
F O R
F R O N T E N D
D E V E LO P E R S
types.
W H AT T H I S TA L K
I S N ’ T
A B O U T
Before I get into all of that, I think it’s important to set expectations. So here’s what this talk isn’t: it’s not a full comparison of each option we looked at.
It’s not a deep dive into features of one choice or the other. And I’m not here to start an argument about why you should use types or not; I think you
should. We can disagree. That’s cool. We can still be friends.
W H AT T H I S TA L K
I S
A B O U T
This talk _is_ about why we chose types. I want to talk about the benefits we were looking for, and the tradeoffs we had to make.
There are code examples in a couple of different languages, some of the syntax might to be unfamiliar, but I’ll do my best to explain everything.
S O M E BAC KG R O U N D
First, a little background on exactly what I mean when I talk about types. There’s a lot of different ways people use to describe type systems: strong vs
weak, static vs dynamic. Strong and Weak are more subjective and have looser definitions; Static & Dynamic are more well defined.
D Y N A M I C T Y P E S
So what do we mean when we say a language is dynamically typed?
T Y P E S A R E C H E C K E D
AT
R U N T I M E
Dynamically typed languages have the types of their values checked at runtime. Another important point is that VALUES have types, but functions or
variables do not.
l e t m y V a r = t r u e ;
We can declare a variable `myVar` and assign it to `true`
l e t m y V a r = t r u e ;
/ / …
m y V a r = 1 0 ;
Later in the code, we can take that same variable and assign it to 10. Nothing about that `myVar` stops us from doing this because `myVar` doesn’t have
a type; however `true` is definitely and boolean and `10` a number.
c o n s t f ( x , y ) = > x + y ;
Here we have a function `f`, it has no type and neither do it’s arguments `x` and `y`. `x` and `y` could be numbers, they could be strings, and since it’s
JavaScript the `+` operator will work on just about anything; even if the result is seemingly non-sensical. The key is, this function doesn’t give us much
information about how it should be used; but we probably have some assumptions.
c o n s t f ( x , y ) = > x + y ;
c o n s t t w o = f ( 1 , 1 ) ;
We can use this function to add two numbers
c o n s t f ( x , y ) = > x + y ;
c o n s t t w o = f ( 1 , 1 ) ;
c o n s t g r e e t = f ( ‘ t h i s ’ , ‘ t h a t ’ ) ;
We can use it to concatenate two strings
c o n s t f ( x , y ) = > x + y ;
c o n s t t w o = f ( 1 , 1 ) ;
c o n s t g r e e t = f ( ‘ t h i s ’ , ‘ t h a t ’ ) ;
c o n s t w a t = f ( ' a ' , 1 ) ;
Or combine two values of disparate types. It doesn’t really make sense to combine a string and a number, but through type-coercion this will work just
fine and give back the string “a1”.
Now the fact that this one function can handle all of these inputs can be seen as a strength or a weakness, depending on how you look at it. If you want to
be explicit about what the function expects and returns, then you probably see this as a weakness. However, many would see the flexibility this can
provide as a strength.
S TAT I C T Y P E S
So what do we mean when we say a language is statically typed?
T Y P E S A R E C H E C K E D
AT
CO M P I L E T I M E
Statically typed languages have their types checked at compile time. Values, functions, variables, they all have types and the compiler ensures that they all
line up before the program can even be run.
l e t m y V a r : b o o l e a n = t r u e ;
In this TypeScript/Flow, we declare `myVar`, give it the type `boolean`, that’s what the single colon denotes, and then assign it to the value `true`.
l e t m y V a r : b o o l e a n = t r u e ;
/ / …
m y V a r = 1 0 ;
If we try to reassign `myVar` to 10 later on, we will get a compiler error. We’ve said that `myVar` is a boolean, so it can only hold `true` or `false`
values.
f : : N u m a = > a - > a - > a
f x y = x + y
Here is a same function we had before, but expressed in PureScript (a Haskell-like language that compiles to JS, similar to Elm). PureScript has support for
different number types, and here we are saying we can add any two numbers and give you a number back.
If you’ve never seen PureScript or Elm or Haskell code before, let me break it this down a little. Type declarations are on a separate line than function
declarations. So the type is that first part. The double colon separates the function name from it’s type and `Num a` before that “fat arrow” is a constraint
that means every time you see `a` in the type it’s a Num, which is basically like a interface. The second line defines `f` as taking two arguments `x` &
`y`, and then adds those arguments together.
f : : N u m a = > a - > a - > a
f x y = x + y
l e t t h r e e = f 1 2
We can apply our function to `1` and `2` and everything is fine, in PureScript functions are applied to values using a space.
f : : N u m a = > a - > a - > a
f x y = x + y
l e t t h r e e = f 1 2
l e t n o p e = f “ 1 ” “ 2 ”
However, if we try to use two strings, the compiler will give us an error since `f` only works with numbers.
f x y = x + y
You could also leave the type signature off completely and PureScript will infer the correct type because the compiler knows that `+` only works on
numbers.
T Y P E I N F E R E N C E
This feature is called type inference; when you can omit type annotations and the compiler will do its best to figure everything out.
TypeScript & Flow also have this feature.
l e t m y V a r = t r u e ;
l e t m y V a r : b o o l e a n = t r u e ;
In plain JavaScript, this variable doesn’t have a type. However, if you take the same code and run it through TypeScript or Flow, it will infer that `myVar` is
a boolean, based on it’s initial assignment.
l e t m y V a r = t r u e ;
l e t m y V a r : b o o l e a n = t r u e ;
So, it’s the same as this code with explicit annotations.
R E C A P
Alright, so a quick recap of all that.
D Y N A M I C T Y P E S
A R E C H E C K E D
R U N T I M E
Dynamic languages associate values with types, and then check them at runtime.
S TAT I C T Y P E S A R E
C H E C K E D AT
CO M P I L E T I M E
Static languages assign types to variables, functions, values, etc. and check them at compile time.
A N D F I N A L LY
In case there is any ambiguity in what I am saying…
Y E S
J AVA S C R I P T
H A S
T Y P E S
Just not static types, and that was what we wanted.
B E N E F I T S
So what were some of the benefits we were hoping to get by adding static types?
P R E C I S I O N
The first thing, and for me one of the biggest assets of static typing, is precision.
B E P R E C I S E
A B O U T W H AT W E
S AY W I T H O U R
CO D E
What I mean is that types allow us to be precise about what we are saying with our code.
S T R I N G S
To illustrate what I mean, let’s look at strings.
S T R I N G S
A R E
L I T E R A L LY
T H E
WO R S T
Strings are literally the worst. Consider all the possible values of a string. Are those really ALL valid for your function? Probably not. We assume that only
certain strings are going to be valid. Even with static types, if we use the `string` type, we lose some precision.
Obviously, strings are needed, let’s say you've got some user input or something, fine, but using strings on data you control should be avoided.
t y p e P r o p s = {
i c o n N a m e : s t r i n g ;
}
c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > {
r e t u r n (
< i
c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } >
< / i >
) ;
}
For example, let’s say you have function that renders an icon. You pass it a some props that include an `iconName` that is a string and it adds the
necessary class name to render the icon; here we’re using TypeScript and React.
t y p e P r o p s = {
i c o n N a m e : s t r i n g ;
}
c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > {
r e t u r n (
< i
c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } >
< / i >
) ;
}
We define an type that describe the properties we expect
t y p e P r o p s = {
i c o n N a m e : s t r i n g ;
}
c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > {
r e t u r n (
< i
c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } >
< / i >
) ;
}
And a function that will take those properties and give us a JSX element, with the right class name.
< ! — S o m e o t h e r s t u f f — >
< I c o n i c o n N a m e = ‘ s u p p o r t ’ / >
< ! — M o r e s t u f f — >
And we use it in our app to show the `support` icon. And this works just fine because we have a `support ` class an icon.
< ! — S o m e o t h e r s t u f f — >
< I c o n i c o n N a m e = ‘ a s d f ’ / >
< ! — M o r e s t u f f — >
But what if we give it a string we don’t recognize? `asdf` is a string, so this will type check, but we don’t have an `asdf` class or icon so this breaks at
runtime.
t y p e P r o p s = {
i c o n N a m e : ‘ s u p p o r t ’ | ‘ h o m e ’ | ‘ p e r s o n ’ ;
}
c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > {
r e t u r n (
< i
c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } >
< / i >
) ;
}
We can use a combination of features provided by TypeScript, to be more precise. Here we’re using string literals and union types (some times referred to
as sum types) to limit what values are can actually be used when creating an icon. You can read this as, an `iconName` can be either `support` or `home`
or `person`, that’s it; any thing else won’t compile.
It’s important to note that these are still strings, but the type of `iconName` is no longer string. In Elm or PureScript this could be solved slightly
differently, but we are still looking to be as precise as possible.
< ! — S o m e o t h e r s t u f f — >
< I c o n i c o n N a m e = ‘ s u p p o r t ’ / >
< ! — M o r e s t u f f — >
So our ‘support’ icon compiles just fine
< ! — S o m e o t h e r s t u f f — >
< I c o n i c o n N a m e = ‘ a s d f ’ / >
< ! — M o r e s t u f f — >
But our `asdf` icon fails to compile because `asdf` isn’t a valid icon name.
E N F O R C E
I N VA R I A N T S
This next one might be a little more obvious but types allow us to say exactly what our functions expect, and ensure at compile time, that the values
flowing into (and out of) our functions are exactly what we expect.
We can create a function that only takes numbers, and if we try to give it anything else, we will get an error.
W H AT I F A
S T R I N G
O R
N U M B E R
I S O K
Now you might have a case where you want to let the caller provide a string or a number, not a problem. Flow, TypeScript, Elm, PureScript, they all offer
union types
d a t a M y T y p e = S S t r i n g | N N u m b e r
m y F u n c : : M y T y p e - > B o o l e a n
m y F u n c ( S _ ) = f a l s e
m y F u n c ( N _ ) = t r u e
In PureScript, we can declare a new datatype called `MyType`, which has two constructors: `S` which holds a string and `N` which holds a number.
A quick little aside, `S` and `N` are just names I came up with, they could be anything really.
d a t a M y T y p e = S S t r i n g | N N u m b e r
m y F u n c : : M y T y p e - > S t r i n g
m y F u n c ( S _ ) = “ T h i s i s a s t r i n g ”
m y F u n c ( N _ ) = “ T h i s i s a n u m ”
Then we can declare a function `myFunc` to handle both cases.
d a t a M y T y p e = S S t r i n g | N N u m b e r
m y F u n c : : M y T y p e - > S t r i n g
m y F u n c ( S _ ) = “ T h i s i s a s t r i n g ”
m y F u n c ( N _ ) = “ T h i s i s a n u m ”
We use PureScripts pattern matching to check if the argument passed is `S` or `N`, we don’t actually care about the string or number they hold so we use
`_` and return the appropriate string.
t y p e M y T y p e = s t r i n g | n u m b e r
c o n s t m y F u n c = ( m : M y T y p e ) : b o o l e a n = > {
r e t u r n t y p e o f m = = = ‘ s t r i n g ’
? f a l s e
: t r u e
}
TypeScript and Flow allow us to do the same thing, but with slightly less wrapping/unwrapping. We create `MyType` and say it is either a string or a
number, no extra constructors are needed.
t y p e M y T y p e = s t r i n g | n u m b e r
c o n s t m y F u n c = ( m : M y T y p e ) : s t r i n g = > {
r e t u r n t y p e o f m = = = ‘ s t r i n g ’
? ‘ T h i s i s a s t r i n g ’
: ‘ T h i s i s a n u m b e r ’ ;
}
The `myFunc` function accepts a `MyType` value (which we call `m`), and we check the type of `m` using the `typeof` operator and return the correct
string.
W H AT A B O U T
O P T I O N A L
A R G U M E N T S
Functions that take optional arguments are fairly common in JavaScript, so how can we handle that with types? Luckily, all of the options we looked at have
some what to say that a value is optional.
c o n s t i n c = ( n ? : n u m b e r ) = > {
r e t u r n n = = u n d e f i n e d
? 1
: n + 1 ;
}
In TypeScript, I can define an increment function and place a `?` after the identifier we want to make optional. If the caller doesn’t supply a number, we
return 1, otherwise we increment whatever was passed in.
c o n s t i n c = ( n ? : n u m b e r ) = > {
r e t u r n n ! = n u l l
? n + 1
: 1 ;
}
TypeScript can also enforce null checks if we add the `strictNullChecks` compiler option. With that option if we leave off the null check, the code won’t
compile.
d a t a M a y b e a = N o t h i n g | J u s t a
PureScript has something similar: a datatype called `Maybe` that we can use to show that something may or may not exist. A value of `Maybe` is either
Nothing, similar to null, or Just the value.
i n c : : M a y b e I n t e g e r - > I n t e g e r
i n c N o t h i n g = 1
i n c ( J u s t n ) = n + 1
The `inc` function, in PureScript, takes a Maybe Integer and returns an Integer. Again, we can use pattern matching to check for both cases. Sending in
`Nothing` gives us 1 and sending a number wrapped in a `Just` means we take that number out and add one to it. One nice thing about PureScript is that
the compiler always reminds us if we don’t handle all possible cases, unless we specifically mark our function as Partial.
c o n s t i n c = ( n ? : n u m b e r ) = > {
r e t u r n n = = n u l l
? 1
: n + 1 ;
}
Interestingly, Flow has both options available and the syntactic difference is subtle. Here we are telling the compiler that `n` is optional, but if it is
provided it must be a number.
c o n s t i n c = ( n : ? n u m b e r ) = > {
r e t u r n n = = n u l l
? 1
: n + 1 ;
}
And here we are telling the compiler that `n` must be provided, but it can a number or null.
c o n s t i n c = ( n ? : n u m b e r ) = > {
r e t u r n n = = n u l l
? 1
: n + 1 ;
}
This is an optional type in flow.
c o n s t i n c = ( n : ? n u m b e r ) = > {
r e t u r n n = = n u l l
? 1
: n + 1 ;
}
And this is a Maybe type in flow.
T E S T
R E D U C T I O N
The next benefit we were looking for was test reduction. But we weren’t trying to completely get rid of having to write tests; having types, doesn’t mean
you don’t have tests.
T Y P E S
T E S T S
So it’s not like this.
T Y P E S
&
T E S T S
It’s more like this. Types work together w/ tests to give confidence in our solutions.
T Y P E S
E N A B L E
M O R E
F O C U S E D
T E S T S
Types allow us to focus on the stuff that our code actually does, not the junk it has to do.
c o n s t f u l l N a m e = ( f , l ) = > {
r e t u r n ` $ { f } $ { l } ` ;
}
Back in plain ol’ JavaScript, let’s say we have a function that takes a `firstName` and a `lastName`, combines them and returns the result. We expect that
the caller is going to send us string, but what happens if they call it without anything?
c o n s t f u l l N a m e = ( f , l ) = > {
i f ( f = = n u l l | | l = = n u l l ) {
r e t u r n ‘ F i r s t & l a s t a r e r e q u i r e d ’ ;
}
r e t u r n ` $ { f } $ { l } ` ;
}
Well we get a runtime error. So, we add in a runtime check to ensure `f` and `l` aren’t null or undefined, and we add a test as well. Except, we’re not
adding one test, we need to consider all of the possible combinations here. A lot is hidden in that condition.
OK. Cool.
But what about numbers?
c o n s t f u l l N a m e = ( f , l ) = > {
i f ( f = = n u l l | | l = = n u l l ) {
r e t u r n ‘ F i r s t & l a s t a r e r e q u i r e d ’ ;
}
i f ( t y p e o f f = = ‘ n u m b e r ’ | | t y p e o f l = = ‘ n u m b e r ’ ) {
r e t u r n ‘ F i r s t & l a s t m u s t b e s t r i n g s ’ ;
}
r e t u r n ` $ { f } $ { l } ` ;
}
No problem, we’ll just add another runtime check…and some more tests.
And there are more things that we’d want to check, but this is already starting to be a little much, right? Sure, we would probably refactor this, it’s a little
redundant, but all we wanted to do was
c o n s t f u l l N a m e = ( f , l ) = > {
r e t u r n ` $ { f } $ { l } ` ;
}
This. And this is all we should really be testing. But since we don’t know anything about `f` or `l`, we have to perform runtime checks to find out what
values were dealing with and, of course, that stuff needs to be tested. However…
c o n s t f u l l N a m e = ( f : s t r i n g , l : s t r i n g ) : s t r i n g = > {
r e t u r n ` $ { f } $ { l } ` ;
}
With something more like this, we could say that fullName requires 2 string arguments, and will give a string back. Those types don’t mean we aren’t
writing tests to make sure our function works properly, though, they don’t tell us anything about what the function actually does, but that’s OK because
that’s the important stuff anyway. That’s what we should be testing.
S H A P E S
Another big thing for us was the ability to define the shape of the data we were concerned with, rather than it’s name.
Let’s take that `fullName` function little further, what if it took off the first name and last name from some object and then concatenated the two?
c o n s t f u l l N a m e = ( o b j : ? ? ? ) : s t r i n g = > {
r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ;
}
In TypeScript/Flow, it would look something like this. We take in one ‘thing’ called `obj`, get the first and last name and concatenate them.
c o n s t f u l l N a m e = ( o b j : ? ? ? ) : s t r i n g = > {
r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ;
}
But what type do we give our input? What do we call that thing?
t y p e P a t i e n t = {
f i r s t N a m e : s t r i n g ;
l a s t N a m e : s t r i n g ;
a g e : n u m b e r ;
/ / …
}
c o n s t f u l l N a m e = ( o b j : P a t i e n t ) : s t r i n g = > {
r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ;
}
Well, we were writing an application that dealt with Patients, so we called it a Patient and we gave it all of the properties a Patient has including a first & last
name. But that has a couple of problems.
P R O B L E M
O N E
The first problem was pretty obvious if you think about it. We just coupled our `fullName` function directly to a Patient, what happens if we want to use
the same function on a Doctor?
t y p e P e r s o n = {
f i r s t N a m e : s t r i n g ;
l a s t N a m e : s t r i n g ;
a g e : n u m b e r ;
/ / …
}
t y p e P a t i e n t = P e r s o n & { … } 

t y p e D o c t o r = P e r s o n & { … }
c o n s t f u l l N a m e = ( o b j : P e r s o n ) : s t r i n g = > {
r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ;
}
One solution is to move fields that are common to Doctors and Patients into a new type called Person, and then use intersection or product types, that little
`&` there, to create special Patient & Doctor types. So we say a Patient or Doctor is a Person & some other properties. We could do the same thing with
interfaces and extension, or with classes and inheritance.
I N H E R I TA N C E
I S
L I T E R A L LY
T H E
WO R S T
But
P R O B L E M
T W O
Moving shared properties to a new type is good, but it doesn’t solve the other problem: our function is asking for way more than it needs. That Person type
could have all kinds of properties on it, but all that function needs is something with a first and last name.
c o n s t f u l l N a m e = ( o b j : { f i r s t N a m e : s t r i n g , l a s t n a m e : s t r i n g } ) : s t r i n g = >
{
r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ;
}
Alternatively, we could do something like this.
Here we’re just specifying the shape of our input. Now, anything that has at least a `firstName` property and a `lastName` property, both of which are
strings, will work.
S T R U C T U R A L
T Y P I N G
Typescript calls this `structural typing`, you may have also heard it called ‘row polymorphism’; you can think of it as static duck-typing. We simply specify
the shape our input objects should have.
c o n s t f u l l N a m e = ( o b j : { f i r s t N a m e : s t r i n g , l a s t n a m e : s t r i n g } ) : s t r i n g = >
{
r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ;
}
Now, code like this can get a little verbose since we need to describe all of the properties we expect. Here, we’re only dealing with first and last name and
it’s already starting to be a bit much.
t y p e N a m e d = {
f i r s t N a m e : s t r i n g ;
l a s t N a m e : s t r i n g ;
}
c o n s t f u l l N a m e = ( o b j : N a m e d ) : s t r i n g = > {
r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ;
}
But we can just move those properties into they’re own type and use that type in type in our function instead. This is similar to when we created a Person
type, just at a more granular level.
i m p o r t N a m e d f r o m ‘ . / t y p e s ’
t y p e P a t i e n t = N a m e d & { … } 

t y p e D o c t o r = N a m e d & { … }
If we exported that new type, we could import it elsewhere and use it to extend other types, that would reduce duplication and promote code reuse, but
that’s not the key take away. The key is that we are simply describing the shape of the data our function needs; giving it a name doesn’t change anything,
you don’t need to use the `Named` type to use the `fullName` function, any data with a first & last name will work just fine.
f u l l N a m e : : f o r a l l p .
{ F i r s t N a m e : : S t r i n g
, L a s t N a m e : : S t r i n g
| p }
- > S t r i n g
f u l l N a m e o b j = o b j . F i r s t N a m e < > o b j . L a s t N a m e
Flow has the same feature and the syntax looks exactly as you might expect.
PureScript also has a similar feature, but with slightly different syntax. Here the type is saying that `fullName` can take objects that have a FirstName &
LastName property (both being a string) and any other properties,
f u l l N a m e : : f o r a l l p .
{ F i r s t N a m e : : S t r i n g
, L a s t N a m e : : S t r i n g
| p }
- > S t r i n g
f u l l N a m e o b j = o b j . F i r s t N a m e < > o b j . L a s t N a m e
We give that bag of properties a name, `p`, and denote it with the `forall p` and `| p`. Now that seems a little tedious, but were we to leave that off, our
input would need to be an object with _only_ first and last name properties.
D E S C R I B E T H E
S H A P E O F O U R
DATA , N OT I T S
N A M E
This idea, that we could describe the shape of our data, was huge for our team. It allows us to write functions that say exactly what they need and, with
some degree of confidence, say what that function might do because we’ve limited it’s scope. This is particularly true in languages like PureScript or Elm
which restrict side-effects and enforce immutability.
R E A S O N I N G
Basically, what this all boils down to the ability to reason about your code, which, I know, has become a bit of a cliche in programming.
U N D E R S TA N D
W H AT CO D E I S
G O I N G TO D O
W I T H O U T
R U N N I N G I T
But, honestly, this is what it all comes down to: being able to be precise with the types in our code, being able to enforce invariants, ensure that functions
only know as much about the data they are working on as needed. All of that aids in your ability to understand what your code is going to. Of course, your
ability to understand what is going on changes between languages: Elm offers more than TypeScript or Flow, and PureScript offers more than Elm, but all of
them increase your ability to reason about your code, and that is a huge benefit for our team.
TO O L I N G
Lastly, I have to mention the tooling. I’m not huge on tooling as an argument for static types, hence the small heading, but it’s still a thing and static types
definitely enable better tooling. However, with new features like destructuring & default arguments, and tools like Tern you can write just JavaScript and
get really nice editor integration. Not to mention the integrations already available. So while static types help make tooling better, there are plenty of
options for JavaScript.
T R A D E O F F S
So if those are some of the benefits, what are some of the negative aspects of adding types?
U N FA M I L I A R I T Y
The first thing to consider is how easy the type system is going to be for a new member of the team to pickup, and this really goes to how familiar the
syntax is. TypeScript or Flow is going to be more familiar to someone with a Java or C# background. While PureScript or Elm will be more familiar to
someone coming from Haskell. And neither are going to be terribly familiar to someone with no previous experience with any type system; though
TypeScript & Flow will probably be more familiar since it’s just JavaScript. But everything was unfamiliar to us at some point, so while there will be some
ramp up time, people will get familiar with the language; it just takes time.
V E R B O S I T Y
Adding type annotations is going to increase how verbose your code is, especially if you move to Flow or TypeScript & especially when you consider
generics. A language like PureScript can help, since the types and function definitions are separated so types are mixed in with function definitions and
good type inference can reduce how much extra typing has to be done, but ultimately there is just more information and that’s going to take up more
space.
A N OT H E R
B U I L D
S T E P
To be honest, this wasn’t really an issue for our team. We already knew we were going to have some kind of build step. Even if we went with no types, we’d
still want to look at code splitting, minification, transpiliation. But if you’re not already building JS, this is something to consider.
I N T E R O P
One big question we had as a team was, ‘How does the work with other JS libraries’. TypeScript & Flow both handle this in a similar way:
D E F I N I T I O N
F I L E S
Definition files. Basically, you can grab a module not written with Flow/TypeScript annotations, and separately pull down a file that defines the types used
in that module. Definitions files are awesome, and super helpful, but they do come with a few problems
P R O B L E M S
W I T H
D E F I N I T I O N S
The first thing to keep in mind is that definition files aren’t authored by the same person as the module; it’s generally another developer lending a hand.
Unfortunately, that means they might not stay in-sync with the module since they are developed separately. If you’re like we were early on, and always on
the latest version of modules, you could find yourself out of sync pretty often. We solved that by simply locking down our dependencies to a specific
known-good version; something we needed to do at some point in the project any way.
Secondly, there is always the possibility that there is no definition file for the library you are using. In that case, there are two options. One is to just use
the `any` type, which in my opinion is a non-starter. The other, which we've taken more than one, is to author your own definition file; which is what we
did.
A N Y
Both Flow & TypeScript have a way to forego type checking: the `any` type. If you add the `any` annotation, no type checking takes place for that value/
function/whatever. It seems like a great idea for quick prototyping, or when you want to work with some library that doesn’t have a definition file written.
A N Y
I S
L I T E R A L LY
T H E
WO R S T
Let’s consider for a moment that we’ve already made the decision to add static types to our JavaScript, why would want to disable checking? We throw out
all of the benefits we get from static typing.
A N Y
A N D
I N F E R E N C E
Flow and TypeScript both have the `any` type and they both have type inference, but how they handle `any` when inferring the types in your code is
different.
F LOW
O N LY I N F E R S
A N Y
F O R
L I B R A R I E S
T H AT H AV E
N O T Y P E S D E F I N E D
Flow will not infer the `any` type for your code, that doesn’t stop you from explicitly adding `any` to something when the compiler can’t figure out the
type. However, it will infer `any` for a third party library that has no accompanying type definitions.
T Y P E S C R I P T
W I L L
H A P P I LY
I N F E R A N Y
Right out of the box, TypeScript will happily infer `any` when the compiler can’t figure out a more precise type. It does this instead of throwing error.
YO U
C A N
&
S H O U L D
D I S A B L E
T H AT
Luckily, TypeScript comes with a way to opt-out of this in the form of a compiler option called `no-implicity-any`. So instead of choosing `any` the
TypeScript compiler will give you an error; you can still be explicit and add `any`. Of course, you should never do that because any is literally the worst.
W H Y S H O U L D
A N Y
B E
AVO I D E D
Let’s take a look at a more concrete example of why `any` should be avoided.
f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … }
Here’s a function that, more or less, comes from our project, and I’d like to use it to illustrate some of the problems with `any`. But first, a little detour,
because this function uses an advanced feature of TypeScript/Flow called generics, which needs a little bit of explanation first.
f u n c t i o n i d < T > ( a r g : T ) : T = > a r g ;
Generics, this very simple case, are used to let the caller of the function determine either the argument type and/or its return type.
In this example we define a generic identity function, it just returns whatever you send it. What we are saying is this `id` function works on any type `T`,
called a type variable. Generics let us enforce that the return type & argument type are the same. The argument must be of type `T` and so must the
return value `T`. Generics are a much more powerful, but that’s a topic for another time.
f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … }
Ok so back to our function
f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … }
We want to make a `Thing`, which is just an object that holds a value of type T.
f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … }
And it does that by accepting any number of arguments of any type.
W H AT ?
What?
H O W ?
Like…how? No seriously, I want to know. How do you go from any number of arguments, of any type, to an object of a specific type?
I’ll tell you
YO U D O N ’ T
You don’t. At least not without being implicit about what at least one of the arguments passed in is.
T H AT T Y P E I S A L I E
That type is a lie. I mean, it’s totally valid, don’t get me wrong…but it’s still lying to you. That type isn’t being honest about the arguments in expects.
There are a bunch of ways to improve that type, the simplest, and the one that our team really needed, is
f u n c t i o n m a k e T h i n g < T > ( a r g : T ) : T h i n g < T > { … }
This one. All we really need to do was wrap a value of type T, in an object. And we want to ensure that the argument passed in is a `T` so we don’t wrap
up something we don’t want.
A N Y
L I K E I N H E R I TA N C E & S T R I N G S
H A S I T ’ S P L AC E
Any, like inheritance and strings has it’s place. But when you use it, it greatly inhibits everything we hope to gain from static typing.
CO N C L U S I O N
Alright so let’s wrap this up. What do we get by adding types
P R E C I S I O N
Types allow us to be precise about our function inputs and outputs and define types that actually express what we want.
I N VA R I A N T S
By being explicit about our inputs and outputs, we can enforce invariants. If we supply the wrong types of values, our code simply won’t compile.
T E S T R E D U C T I O N
All of this helps reduce the number of tests we have to write, and allows us to focus on the tests we actually need to write.
R E A S O N I N G
Test reduction is a great benefit, but the real draw, in my opinion, is the understanding that comes when static types get added to the mix.
O F CO U R S E
T H E R E A R E
T R A D E O F F S
Of course, there are tradeoffs.
R A M P U P T I M E
Depending on experience level & background there is going to be some increased ramp-up time; especially if your team has never dealt with static type
systems before.
V E R B O S I T Y
Adding static type information means adding more code, there’s no two ways about it. Type inference helps, but at the end of the day there is just more
code to read and write.
B U I L D I N G
Honestly, I can’t think of any project that doesn’t already have a build step, but if you don’t, adding static types will make you add one.
I N T E R O P
For me, this is the one of the biggest tradeoffs and one we didn’t fully appreciate. If you’re using large, well-known libraries there is likely a typed version,
or type definitions for it. But that isn’t always the case, especially for smaller libraries. Luckily, there are options. The one our team prefers is writing our
own definitions for the bits we need. It takes extra time, but it’s far safer.
A N Y
Lastly, `any`. It disables type checking, and really cuts into the benefits that static types are meant to bring. If you’re using TypeScript, don’t let the
compiler infer it. If you’re using Flow, add a definition file to any third-party lib you use. If you use Elm or PureScript…nevermind then; they don’t have this
problem.
I S I T
WO R T H I T
Is it worth it? Obviously I think so, right? Our team started using TypeScript toward the end of 2016 and we’ve gone through a lot of the pains outlined
here. We’ve hit issues with ramp up time for new developers. We’ve had to get used to writing more verbose code and we’ve fought the urge to just at
`any` whenever things got a little to annoying. And we did all of that because of what we gained by introducing types far outweighed any of the pain we
had to go through. We have more confidence in what our code does, our tests focus more on the logic of our code than before, and developers that have
never been in our code before can quickly figure out what is going on. All because we decided to add types.
G I V E I T A S H OT
If you haven’t already, you should give it a shot. Maybe going all the way to PureScript or Elm is going to work for you, it didn’t for us (despite my best
efforts), so check out TypeScript or Flow. Find a small project to try it out on, or a big project like we did. Build a single piece of an existing project using
types. Whatever. I think you’ll find that what you gain is well worth any trade-offs you have to make.
T Y P E S C R I P T
H T T P S : / / W W W.T Y P E S C R I P T L A N G .O R G
F LOW
H T T P S : / / F LO W.O R G /
E L M
H T T P : / / E L M - L A N G .O R G /
P U R E S C R I P T
H T T P : / / W W W. P U R E S C R I P T.O R G /
T Y P E S
H T T P S : / / W W W. D E S T R O YA L L S O F T WA R E .CO M / CO M P E N D I U M / T Y P E S
T Y P E S A S D E S I G N TO O L S
H T T P S : / / W W W.YO U T U B E .CO M / WATC H ? V = 6 M U AV D 6 I 4 O U
W H Y U S E S TAT I C
T Y P E S I N J AVA S C R I P T
H T T P S : / / M E D I U M . F R E E CO D E C A M P.O R G / W H Y - U S E - S TAT I C -T Y P E S - I N -
J AVA S C R I P T- PA R T- 1 - 8 3 8 2 DA 1 E 0 A D B
Gary Bernhardt
Kris Jenkins
Preethi Kasireddy
T H A N K YO U
@ t h u n k _ l i f e
Thank you very much.

Más contenido relacionado

La actualidad más candente

String Matching with Finite Automata,Aho corasick,
String Matching with Finite Automata,Aho corasick,String Matching with Finite Automata,Aho corasick,
String Matching with Finite Automata,Aho corasick,
8neutron8
 
Introduction to the theory of computation
Introduction to the theory of computationIntroduction to the theory of computation
Introduction to the theory of computation
prasadmvreddy
 

La actualidad más candente (20)

RegexCat
RegexCatRegexCat
RegexCat
 
python isn't just a snake
python isn't just a snakepython isn't just a snake
python isn't just a snake
 
Module 11
Module 11Module 11
Module 11
 
Finite automata
Finite automataFinite automata
Finite automata
 
String Matching with Finite Automata,Aho corasick,
String Matching with Finite Automata,Aho corasick,String Matching with Finite Automata,Aho corasick,
String Matching with Finite Automata,Aho corasick,
 
Ch3
Ch3Ch3
Ch3
 
Theory of Computation - Lectures 4 and 5
Theory of Computation - Lectures 4 and 5Theory of Computation - Lectures 4 and 5
Theory of Computation - Lectures 4 and 5
 
Lecture 07 08 syntax analysis-4
Lecture 07 08 syntax analysis-4Lecture 07 08 syntax analysis-4
Lecture 07 08 syntax analysis-4
 
Milano JS Meetup - Gabriele Petronella - Codemotion Milan 2016
Milano JS Meetup -  Gabriele Petronella - Codemotion Milan 2016Milano JS Meetup -  Gabriele Petronella - Codemotion Milan 2016
Milano JS Meetup - Gabriele Petronella - Codemotion Milan 2016
 
Introduction to the theory of computation
Introduction to the theory of computationIntroduction to the theory of computation
Introduction to the theory of computation
 
Cse lecture-6-c control statement
Cse lecture-6-c control statementCse lecture-6-c control statement
Cse lecture-6-c control statement
 
Finite Automata in compiler design
Finite Automata in compiler designFinite Automata in compiler design
Finite Automata in compiler design
 
Types, classes and concepts
Types, classes and conceptsTypes, classes and concepts
Types, classes and concepts
 
Theory of computation and automata
Theory of computation and automataTheory of computation and automata
Theory of computation and automata
 
Conditionalstatement
ConditionalstatementConditionalstatement
Conditionalstatement
 
Lecture 2 keyword of C Programming Language
Lecture 2 keyword of C Programming LanguageLecture 2 keyword of C Programming Language
Lecture 2 keyword of C Programming Language
 
Introduction to fa and dfa
Introduction to fa  and dfaIntroduction to fa  and dfa
Introduction to fa and dfa
 
Flow of control by deepak lakhlan
Flow of control by deepak lakhlanFlow of control by deepak lakhlan
Flow of control by deepak lakhlan
 
Python programming language
Python programming languagePython programming language
Python programming language
 
Left factor put
Left factor putLeft factor put
Left factor put
 

Similar a Types For Frontend Developers

Python+Syntax+Cheat+Sheet+Booklet.pdf
Python+Syntax+Cheat+Sheet+Booklet.pdfPython+Syntax+Cheat+Sheet+Booklet.pdf
Python+Syntax+Cheat+Sheet+Booklet.pdf
Ganteng tengab
 
009 Data Handling cs class 12 cbse mount litera
009 Data Handling cs class 12 cbse mount litera009 Data Handling cs class 12 cbse mount litera
009 Data Handling cs class 12 cbse mount litera
RithinA1
 
The Java Script Programming Language
The  Java Script  Programming  LanguageThe  Java Script  Programming  Language
The Java Script Programming Language
zone
 
Javascript by Yahoo
Javascript by YahooJavascript by Yahoo
Javascript by Yahoo
birbal
 
Real World Haskell: Lecture 4
Real World Haskell: Lecture 4Real World Haskell: Lecture 4
Real World Haskell: Lecture 4
Bryan O'Sullivan
 
1_1_python-course-notes-sections-1-7.pdf
1_1_python-course-notes-sections-1-7.pdf1_1_python-course-notes-sections-1-7.pdf
1_1_python-course-notes-sections-1-7.pdf
Javier Crisostomo
 

Similar a Types For Frontend Developers (20)

Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
 
Python+Syntax+Cheat+Sheet+Booklet.pdf
Python+Syntax+Cheat+Sheet+Booklet.pdfPython+Syntax+Cheat+Sheet+Booklet.pdf
Python+Syntax+Cheat+Sheet+Booklet.pdf
 
JavaScript: Core Part
JavaScript: Core PartJavaScript: Core Part
JavaScript: Core Part
 
009 Data Handling cs class 12 cbse mount litera
009 Data Handling cs class 12 cbse mount litera009 Data Handling cs class 12 cbse mount litera
009 Data Handling cs class 12 cbse mount litera
 
Spreadsheets for developers
Spreadsheets for developersSpreadsheets for developers
Spreadsheets for developers
 
R Programming
R ProgrammingR Programming
R Programming
 
Ember js meetup treviso liquid-fire
Ember js meetup treviso liquid-fireEmber js meetup treviso liquid-fire
Ember js meetup treviso liquid-fire
 
Learning R while exploring statistics
Learning R while exploring statisticsLearning R while exploring statistics
Learning R while exploring statistics
 
How to Program
How to ProgramHow to Program
How to Program
 
Javascript
JavascriptJavascript
Javascript
 
The Java Script Programming Language
The  Java Script  Programming  LanguageThe  Java Script  Programming  Language
The Java Script Programming Language
 
Les origines de Javascript
Les origines de JavascriptLes origines de Javascript
Les origines de Javascript
 
Javascript by Yahoo
Javascript by YahooJavascript by Yahoo
Javascript by Yahoo
 
The JavaScript Programming Language
The JavaScript Programming LanguageThe JavaScript Programming Language
The JavaScript Programming Language
 
Javascript
JavascriptJavascript
Javascript
 
Data weave 2.0 advanced (recursion, pattern matching)
Data weave 2.0   advanced (recursion, pattern matching)Data weave 2.0   advanced (recursion, pattern matching)
Data weave 2.0 advanced (recursion, pattern matching)
 
Real World Haskell: Lecture 4
Real World Haskell: Lecture 4Real World Haskell: Lecture 4
Real World Haskell: Lecture 4
 
1_1_python-course-notes-sections-1-7.pdf
1_1_python-course-notes-sections-1-7.pdf1_1_python-course-notes-sections-1-7.pdf
1_1_python-course-notes-sections-1-7.pdf
 
Type Systems on the example of TypeScript
Type Systems on the example of TypeScriptType Systems on the example of TypeScript
Type Systems on the example of TypeScript
 
The Swift Programming Language - Xcode6 for iOS App Development - AgileInfowa...
The Swift Programming Language - Xcode6 for iOS App Development - AgileInfowa...The Swift Programming Language - Xcode6 for iOS App Development - AgileInfowa...
The Swift Programming Language - Xcode6 for iOS App Development - AgileInfowa...
 

Último

Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
chiefasafspells
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
masabamasaba
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 

Último (20)

WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 

Types For Frontend Developers

  • 1. T Y P E S F O R F R O N T E N D D E V E LO P E R S In mid-2016, my team and I were a few years in to an Angular 1 project, and despite our best efforts it was starting to get a little unruly. Sure we had guidelines we followed, high test coverage, a linter making sure we stayed in the lines, but we never really had confidence in what our code was doing. Despite our best efforts, our code wasn’t terribly clear and our tests were riddled with boilerplate. It was next to impossible to take a break from some part of the code, come back and understand, in a reasonable amount of time, what the code was doing. Luckily, at that time we were going to start a new project and we had complete freedom to figure out the stack. We looked at new frameworks, different languages all the while keeping in mind that whatever we chose needed to help us make our code more understandable and easier to test. To that end, we started looking at
  • 2. T Y P E S F O R F R O N T E N D D E V E LO P E R S types.
  • 3. W H AT T H I S TA L K I S N ’ T A B O U T Before I get into all of that, I think it’s important to set expectations. So here’s what this talk isn’t: it’s not a full comparison of each option we looked at. It’s not a deep dive into features of one choice or the other. And I’m not here to start an argument about why you should use types or not; I think you should. We can disagree. That’s cool. We can still be friends.
  • 4. W H AT T H I S TA L K I S A B O U T This talk _is_ about why we chose types. I want to talk about the benefits we were looking for, and the tradeoffs we had to make. There are code examples in a couple of different languages, some of the syntax might to be unfamiliar, but I’ll do my best to explain everything.
  • 5. S O M E BAC KG R O U N D First, a little background on exactly what I mean when I talk about types. There’s a lot of different ways people use to describe type systems: strong vs weak, static vs dynamic. Strong and Weak are more subjective and have looser definitions; Static & Dynamic are more well defined.
  • 6. D Y N A M I C T Y P E S So what do we mean when we say a language is dynamically typed?
  • 7. T Y P E S A R E C H E C K E D AT R U N T I M E Dynamically typed languages have the types of their values checked at runtime. Another important point is that VALUES have types, but functions or variables do not.
  • 8. l e t m y V a r = t r u e ; We can declare a variable `myVar` and assign it to `true`
  • 9. l e t m y V a r = t r u e ; / / … m y V a r = 1 0 ; Later in the code, we can take that same variable and assign it to 10. Nothing about that `myVar` stops us from doing this because `myVar` doesn’t have a type; however `true` is definitely and boolean and `10` a number.
  • 10. c o n s t f ( x , y ) = > x + y ; Here we have a function `f`, it has no type and neither do it’s arguments `x` and `y`. `x` and `y` could be numbers, they could be strings, and since it’s JavaScript the `+` operator will work on just about anything; even if the result is seemingly non-sensical. The key is, this function doesn’t give us much information about how it should be used; but we probably have some assumptions.
  • 11. c o n s t f ( x , y ) = > x + y ; c o n s t t w o = f ( 1 , 1 ) ; We can use this function to add two numbers
  • 12. c o n s t f ( x , y ) = > x + y ; c o n s t t w o = f ( 1 , 1 ) ; c o n s t g r e e t = f ( ‘ t h i s ’ , ‘ t h a t ’ ) ; We can use it to concatenate two strings
  • 13. c o n s t f ( x , y ) = > x + y ; c o n s t t w o = f ( 1 , 1 ) ; c o n s t g r e e t = f ( ‘ t h i s ’ , ‘ t h a t ’ ) ; c o n s t w a t = f ( ' a ' , 1 ) ; Or combine two values of disparate types. It doesn’t really make sense to combine a string and a number, but through type-coercion this will work just fine and give back the string “a1”. Now the fact that this one function can handle all of these inputs can be seen as a strength or a weakness, depending on how you look at it. If you want to be explicit about what the function expects and returns, then you probably see this as a weakness. However, many would see the flexibility this can provide as a strength.
  • 14. S TAT I C T Y P E S So what do we mean when we say a language is statically typed?
  • 15. T Y P E S A R E C H E C K E D AT CO M P I L E T I M E Statically typed languages have their types checked at compile time. Values, functions, variables, they all have types and the compiler ensures that they all line up before the program can even be run.
  • 16. l e t m y V a r : b o o l e a n = t r u e ; In this TypeScript/Flow, we declare `myVar`, give it the type `boolean`, that’s what the single colon denotes, and then assign it to the value `true`.
  • 17. l e t m y V a r : b o o l e a n = t r u e ; / / … m y V a r = 1 0 ; If we try to reassign `myVar` to 10 later on, we will get a compiler error. We’ve said that `myVar` is a boolean, so it can only hold `true` or `false` values.
  • 18. f : : N u m a = > a - > a - > a f x y = x + y Here is a same function we had before, but expressed in PureScript (a Haskell-like language that compiles to JS, similar to Elm). PureScript has support for different number types, and here we are saying we can add any two numbers and give you a number back. If you’ve never seen PureScript or Elm or Haskell code before, let me break it this down a little. Type declarations are on a separate line than function declarations. So the type is that first part. The double colon separates the function name from it’s type and `Num a` before that “fat arrow” is a constraint that means every time you see `a` in the type it’s a Num, which is basically like a interface. The second line defines `f` as taking two arguments `x` & `y`, and then adds those arguments together.
  • 19. f : : N u m a = > a - > a - > a f x y = x + y l e t t h r e e = f 1 2 We can apply our function to `1` and `2` and everything is fine, in PureScript functions are applied to values using a space.
  • 20. f : : N u m a = > a - > a - > a f x y = x + y l e t t h r e e = f 1 2 l e t n o p e = f “ 1 ” “ 2 ” However, if we try to use two strings, the compiler will give us an error since `f` only works with numbers.
  • 21. f x y = x + y You could also leave the type signature off completely and PureScript will infer the correct type because the compiler knows that `+` only works on numbers.
  • 22. T Y P E I N F E R E N C E This feature is called type inference; when you can omit type annotations and the compiler will do its best to figure everything out. TypeScript & Flow also have this feature.
  • 23. l e t m y V a r = t r u e ; l e t m y V a r : b o o l e a n = t r u e ; In plain JavaScript, this variable doesn’t have a type. However, if you take the same code and run it through TypeScript or Flow, it will infer that `myVar` is a boolean, based on it’s initial assignment.
  • 24. l e t m y V a r = t r u e ; l e t m y V a r : b o o l e a n = t r u e ; So, it’s the same as this code with explicit annotations.
  • 25. R E C A P Alright, so a quick recap of all that.
  • 26. D Y N A M I C T Y P E S A R E C H E C K E D R U N T I M E Dynamic languages associate values with types, and then check them at runtime.
  • 27. S TAT I C T Y P E S A R E C H E C K E D AT CO M P I L E T I M E Static languages assign types to variables, functions, values, etc. and check them at compile time.
  • 28. A N D F I N A L LY In case there is any ambiguity in what I am saying…
  • 29. Y E S J AVA S C R I P T H A S T Y P E S Just not static types, and that was what we wanted.
  • 30. B E N E F I T S So what were some of the benefits we were hoping to get by adding static types?
  • 31. P R E C I S I O N The first thing, and for me one of the biggest assets of static typing, is precision.
  • 32. B E P R E C I S E A B O U T W H AT W E S AY W I T H O U R CO D E What I mean is that types allow us to be precise about what we are saying with our code.
  • 33. S T R I N G S To illustrate what I mean, let’s look at strings.
  • 34. S T R I N G S A R E L I T E R A L LY T H E WO R S T Strings are literally the worst. Consider all the possible values of a string. Are those really ALL valid for your function? Probably not. We assume that only certain strings are going to be valid. Even with static types, if we use the `string` type, we lose some precision. Obviously, strings are needed, let’s say you've got some user input or something, fine, but using strings on data you control should be avoided.
  • 35. t y p e P r o p s = { i c o n N a m e : s t r i n g ; } c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > { r e t u r n ( < i c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } > < / i > ) ; } For example, let’s say you have function that renders an icon. You pass it a some props that include an `iconName` that is a string and it adds the necessary class name to render the icon; here we’re using TypeScript and React.
  • 36. t y p e P r o p s = { i c o n N a m e : s t r i n g ; } c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > { r e t u r n ( < i c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } > < / i > ) ; } We define an type that describe the properties we expect
  • 37. t y p e P r o p s = { i c o n N a m e : s t r i n g ; } c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > { r e t u r n ( < i c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } > < / i > ) ; } And a function that will take those properties and give us a JSX element, with the right class name.
  • 38. < ! — S o m e o t h e r s t u f f — > < I c o n i c o n N a m e = ‘ s u p p o r t ’ / > < ! — M o r e s t u f f — > And we use it in our app to show the `support` icon. And this works just fine because we have a `support ` class an icon.
  • 39. < ! — S o m e o t h e r s t u f f — > < I c o n i c o n N a m e = ‘ a s d f ’ / > < ! — M o r e s t u f f — > But what if we give it a string we don’t recognize? `asdf` is a string, so this will type check, but we don’t have an `asdf` class or icon so this breaks at runtime.
  • 40. t y p e P r o p s = { i c o n N a m e : ‘ s u p p o r t ’ | ‘ h o m e ’ | ‘ p e r s o n ’ ; } c o n s t I c o n = ( p r o p s : P r o p s ) : E l e m e n t = > { r e t u r n ( < i c l a s s N a m e = { ` s l $ { p r o p s . i c o n N a m e ` } > < / i > ) ; } We can use a combination of features provided by TypeScript, to be more precise. Here we’re using string literals and union types (some times referred to as sum types) to limit what values are can actually be used when creating an icon. You can read this as, an `iconName` can be either `support` or `home` or `person`, that’s it; any thing else won’t compile. It’s important to note that these are still strings, but the type of `iconName` is no longer string. In Elm or PureScript this could be solved slightly differently, but we are still looking to be as precise as possible.
  • 41. < ! — S o m e o t h e r s t u f f — > < I c o n i c o n N a m e = ‘ s u p p o r t ’ / > < ! — M o r e s t u f f — > So our ‘support’ icon compiles just fine
  • 42. < ! — S o m e o t h e r s t u f f — > < I c o n i c o n N a m e = ‘ a s d f ’ / > < ! — M o r e s t u f f — > But our `asdf` icon fails to compile because `asdf` isn’t a valid icon name.
  • 43. E N F O R C E I N VA R I A N T S This next one might be a little more obvious but types allow us to say exactly what our functions expect, and ensure at compile time, that the values flowing into (and out of) our functions are exactly what we expect. We can create a function that only takes numbers, and if we try to give it anything else, we will get an error.
  • 44. W H AT I F A S T R I N G O R N U M B E R I S O K Now you might have a case where you want to let the caller provide a string or a number, not a problem. Flow, TypeScript, Elm, PureScript, they all offer union types
  • 45. d a t a M y T y p e = S S t r i n g | N N u m b e r m y F u n c : : M y T y p e - > B o o l e a n m y F u n c ( S _ ) = f a l s e m y F u n c ( N _ ) = t r u e In PureScript, we can declare a new datatype called `MyType`, which has two constructors: `S` which holds a string and `N` which holds a number. A quick little aside, `S` and `N` are just names I came up with, they could be anything really.
  • 46. d a t a M y T y p e = S S t r i n g | N N u m b e r m y F u n c : : M y T y p e - > S t r i n g m y F u n c ( S _ ) = “ T h i s i s a s t r i n g ” m y F u n c ( N _ ) = “ T h i s i s a n u m ” Then we can declare a function `myFunc` to handle both cases.
  • 47. d a t a M y T y p e = S S t r i n g | N N u m b e r m y F u n c : : M y T y p e - > S t r i n g m y F u n c ( S _ ) = “ T h i s i s a s t r i n g ” m y F u n c ( N _ ) = “ T h i s i s a n u m ” We use PureScripts pattern matching to check if the argument passed is `S` or `N`, we don’t actually care about the string or number they hold so we use `_` and return the appropriate string.
  • 48. t y p e M y T y p e = s t r i n g | n u m b e r c o n s t m y F u n c = ( m : M y T y p e ) : b o o l e a n = > { r e t u r n t y p e o f m = = = ‘ s t r i n g ’ ? f a l s e : t r u e } TypeScript and Flow allow us to do the same thing, but with slightly less wrapping/unwrapping. We create `MyType` and say it is either a string or a number, no extra constructors are needed.
  • 49. t y p e M y T y p e = s t r i n g | n u m b e r c o n s t m y F u n c = ( m : M y T y p e ) : s t r i n g = > { r e t u r n t y p e o f m = = = ‘ s t r i n g ’ ? ‘ T h i s i s a s t r i n g ’ : ‘ T h i s i s a n u m b e r ’ ; } The `myFunc` function accepts a `MyType` value (which we call `m`), and we check the type of `m` using the `typeof` operator and return the correct string.
  • 50. W H AT A B O U T O P T I O N A L A R G U M E N T S Functions that take optional arguments are fairly common in JavaScript, so how can we handle that with types? Luckily, all of the options we looked at have some what to say that a value is optional.
  • 51. c o n s t i n c = ( n ? : n u m b e r ) = > { r e t u r n n = = u n d e f i n e d ? 1 : n + 1 ; } In TypeScript, I can define an increment function and place a `?` after the identifier we want to make optional. If the caller doesn’t supply a number, we return 1, otherwise we increment whatever was passed in.
  • 52. c o n s t i n c = ( n ? : n u m b e r ) = > { r e t u r n n ! = n u l l ? n + 1 : 1 ; } TypeScript can also enforce null checks if we add the `strictNullChecks` compiler option. With that option if we leave off the null check, the code won’t compile.
  • 53. d a t a M a y b e a = N o t h i n g | J u s t a PureScript has something similar: a datatype called `Maybe` that we can use to show that something may or may not exist. A value of `Maybe` is either Nothing, similar to null, or Just the value.
  • 54. i n c : : M a y b e I n t e g e r - > I n t e g e r i n c N o t h i n g = 1 i n c ( J u s t n ) = n + 1 The `inc` function, in PureScript, takes a Maybe Integer and returns an Integer. Again, we can use pattern matching to check for both cases. Sending in `Nothing` gives us 1 and sending a number wrapped in a `Just` means we take that number out and add one to it. One nice thing about PureScript is that the compiler always reminds us if we don’t handle all possible cases, unless we specifically mark our function as Partial.
  • 55. c o n s t i n c = ( n ? : n u m b e r ) = > { r e t u r n n = = n u l l ? 1 : n + 1 ; } Interestingly, Flow has both options available and the syntactic difference is subtle. Here we are telling the compiler that `n` is optional, but if it is provided it must be a number.
  • 56. c o n s t i n c = ( n : ? n u m b e r ) = > { r e t u r n n = = n u l l ? 1 : n + 1 ; } And here we are telling the compiler that `n` must be provided, but it can a number or null.
  • 57. c o n s t i n c = ( n ? : n u m b e r ) = > { r e t u r n n = = n u l l ? 1 : n + 1 ; } This is an optional type in flow.
  • 58. c o n s t i n c = ( n : ? n u m b e r ) = > { r e t u r n n = = n u l l ? 1 : n + 1 ; } And this is a Maybe type in flow.
  • 59. T E S T R E D U C T I O N The next benefit we were looking for was test reduction. But we weren’t trying to completely get rid of having to write tests; having types, doesn’t mean you don’t have tests.
  • 60. T Y P E S T E S T S So it’s not like this.
  • 61. T Y P E S & T E S T S It’s more like this. Types work together w/ tests to give confidence in our solutions.
  • 62. T Y P E S E N A B L E M O R E F O C U S E D T E S T S Types allow us to focus on the stuff that our code actually does, not the junk it has to do.
  • 63. c o n s t f u l l N a m e = ( f , l ) = > { r e t u r n ` $ { f } $ { l } ` ; } Back in plain ol’ JavaScript, let’s say we have a function that takes a `firstName` and a `lastName`, combines them and returns the result. We expect that the caller is going to send us string, but what happens if they call it without anything?
  • 64. c o n s t f u l l N a m e = ( f , l ) = > { i f ( f = = n u l l | | l = = n u l l ) { r e t u r n ‘ F i r s t & l a s t a r e r e q u i r e d ’ ; } r e t u r n ` $ { f } $ { l } ` ; } Well we get a runtime error. So, we add in a runtime check to ensure `f` and `l` aren’t null or undefined, and we add a test as well. Except, we’re not adding one test, we need to consider all of the possible combinations here. A lot is hidden in that condition. OK. Cool. But what about numbers?
  • 65. c o n s t f u l l N a m e = ( f , l ) = > { i f ( f = = n u l l | | l = = n u l l ) { r e t u r n ‘ F i r s t & l a s t a r e r e q u i r e d ’ ; } i f ( t y p e o f f = = ‘ n u m b e r ’ | | t y p e o f l = = ‘ n u m b e r ’ ) { r e t u r n ‘ F i r s t & l a s t m u s t b e s t r i n g s ’ ; } r e t u r n ` $ { f } $ { l } ` ; } No problem, we’ll just add another runtime check…and some more tests. And there are more things that we’d want to check, but this is already starting to be a little much, right? Sure, we would probably refactor this, it’s a little redundant, but all we wanted to do was
  • 66. c o n s t f u l l N a m e = ( f , l ) = > { r e t u r n ` $ { f } $ { l } ` ; } This. And this is all we should really be testing. But since we don’t know anything about `f` or `l`, we have to perform runtime checks to find out what values were dealing with and, of course, that stuff needs to be tested. However…
  • 67. c o n s t f u l l N a m e = ( f : s t r i n g , l : s t r i n g ) : s t r i n g = > { r e t u r n ` $ { f } $ { l } ` ; } With something more like this, we could say that fullName requires 2 string arguments, and will give a string back. Those types don’t mean we aren’t writing tests to make sure our function works properly, though, they don’t tell us anything about what the function actually does, but that’s OK because that’s the important stuff anyway. That’s what we should be testing.
  • 68. S H A P E S Another big thing for us was the ability to define the shape of the data we were concerned with, rather than it’s name. Let’s take that `fullName` function little further, what if it took off the first name and last name from some object and then concatenated the two?
  • 69. c o n s t f u l l N a m e = ( o b j : ? ? ? ) : s t r i n g = > { r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ; } In TypeScript/Flow, it would look something like this. We take in one ‘thing’ called `obj`, get the first and last name and concatenate them.
  • 70. c o n s t f u l l N a m e = ( o b j : ? ? ? ) : s t r i n g = > { r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ; } But what type do we give our input? What do we call that thing?
  • 71. t y p e P a t i e n t = { f i r s t N a m e : s t r i n g ; l a s t N a m e : s t r i n g ; a g e : n u m b e r ; / / … } c o n s t f u l l N a m e = ( o b j : P a t i e n t ) : s t r i n g = > { r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ; } Well, we were writing an application that dealt with Patients, so we called it a Patient and we gave it all of the properties a Patient has including a first & last name. But that has a couple of problems.
  • 72. P R O B L E M O N E The first problem was pretty obvious if you think about it. We just coupled our `fullName` function directly to a Patient, what happens if we want to use the same function on a Doctor?
  • 73. t y p e P e r s o n = { f i r s t N a m e : s t r i n g ; l a s t N a m e : s t r i n g ; a g e : n u m b e r ; / / … } t y p e P a t i e n t = P e r s o n & { … } 
 t y p e D o c t o r = P e r s o n & { … } c o n s t f u l l N a m e = ( o b j : P e r s o n ) : s t r i n g = > { r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ; } One solution is to move fields that are common to Doctors and Patients into a new type called Person, and then use intersection or product types, that little `&` there, to create special Patient & Doctor types. So we say a Patient or Doctor is a Person & some other properties. We could do the same thing with interfaces and extension, or with classes and inheritance.
  • 74. I N H E R I TA N C E I S L I T E R A L LY T H E WO R S T But
  • 75. P R O B L E M T W O Moving shared properties to a new type is good, but it doesn’t solve the other problem: our function is asking for way more than it needs. That Person type could have all kinds of properties on it, but all that function needs is something with a first and last name.
  • 76. c o n s t f u l l N a m e = ( o b j : { f i r s t N a m e : s t r i n g , l a s t n a m e : s t r i n g } ) : s t r i n g = > { r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ; } Alternatively, we could do something like this. Here we’re just specifying the shape of our input. Now, anything that has at least a `firstName` property and a `lastName` property, both of which are strings, will work.
  • 77. S T R U C T U R A L T Y P I N G Typescript calls this `structural typing`, you may have also heard it called ‘row polymorphism’; you can think of it as static duck-typing. We simply specify the shape our input objects should have.
  • 78. c o n s t f u l l N a m e = ( o b j : { f i r s t N a m e : s t r i n g , l a s t n a m e : s t r i n g } ) : s t r i n g = > { r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ; } Now, code like this can get a little verbose since we need to describe all of the properties we expect. Here, we’re only dealing with first and last name and it’s already starting to be a bit much.
  • 79. t y p e N a m e d = { f i r s t N a m e : s t r i n g ; l a s t N a m e : s t r i n g ; } c o n s t f u l l N a m e = ( o b j : N a m e d ) : s t r i n g = > { r e t u r n ` $ { o b j . f i r s t N a m e } $ { o b j . l a s t N a m e } ` ; } But we can just move those properties into they’re own type and use that type in type in our function instead. This is similar to when we created a Person type, just at a more granular level.
  • 80. i m p o r t N a m e d f r o m ‘ . / t y p e s ’ t y p e P a t i e n t = N a m e d & { … } 
 t y p e D o c t o r = N a m e d & { … } If we exported that new type, we could import it elsewhere and use it to extend other types, that would reduce duplication and promote code reuse, but that’s not the key take away. The key is that we are simply describing the shape of the data our function needs; giving it a name doesn’t change anything, you don’t need to use the `Named` type to use the `fullName` function, any data with a first & last name will work just fine.
  • 81. f u l l N a m e : : f o r a l l p . { F i r s t N a m e : : S t r i n g , L a s t N a m e : : S t r i n g | p } - > S t r i n g f u l l N a m e o b j = o b j . F i r s t N a m e < > o b j . L a s t N a m e Flow has the same feature and the syntax looks exactly as you might expect. PureScript also has a similar feature, but with slightly different syntax. Here the type is saying that `fullName` can take objects that have a FirstName & LastName property (both being a string) and any other properties,
  • 82. f u l l N a m e : : f o r a l l p . { F i r s t N a m e : : S t r i n g , L a s t N a m e : : S t r i n g | p } - > S t r i n g f u l l N a m e o b j = o b j . F i r s t N a m e < > o b j . L a s t N a m e We give that bag of properties a name, `p`, and denote it with the `forall p` and `| p`. Now that seems a little tedious, but were we to leave that off, our input would need to be an object with _only_ first and last name properties.
  • 83. D E S C R I B E T H E S H A P E O F O U R DATA , N OT I T S N A M E This idea, that we could describe the shape of our data, was huge for our team. It allows us to write functions that say exactly what they need and, with some degree of confidence, say what that function might do because we’ve limited it’s scope. This is particularly true in languages like PureScript or Elm which restrict side-effects and enforce immutability.
  • 84. R E A S O N I N G Basically, what this all boils down to the ability to reason about your code, which, I know, has become a bit of a cliche in programming.
  • 85. U N D E R S TA N D W H AT CO D E I S G O I N G TO D O W I T H O U T R U N N I N G I T But, honestly, this is what it all comes down to: being able to be precise with the types in our code, being able to enforce invariants, ensure that functions only know as much about the data they are working on as needed. All of that aids in your ability to understand what your code is going to. Of course, your ability to understand what is going on changes between languages: Elm offers more than TypeScript or Flow, and PureScript offers more than Elm, but all of them increase your ability to reason about your code, and that is a huge benefit for our team.
  • 86. TO O L I N G Lastly, I have to mention the tooling. I’m not huge on tooling as an argument for static types, hence the small heading, but it’s still a thing and static types definitely enable better tooling. However, with new features like destructuring & default arguments, and tools like Tern you can write just JavaScript and get really nice editor integration. Not to mention the integrations already available. So while static types help make tooling better, there are plenty of options for JavaScript.
  • 87. T R A D E O F F S So if those are some of the benefits, what are some of the negative aspects of adding types?
  • 88. U N FA M I L I A R I T Y The first thing to consider is how easy the type system is going to be for a new member of the team to pickup, and this really goes to how familiar the syntax is. TypeScript or Flow is going to be more familiar to someone with a Java or C# background. While PureScript or Elm will be more familiar to someone coming from Haskell. And neither are going to be terribly familiar to someone with no previous experience with any type system; though TypeScript & Flow will probably be more familiar since it’s just JavaScript. But everything was unfamiliar to us at some point, so while there will be some ramp up time, people will get familiar with the language; it just takes time.
  • 89. V E R B O S I T Y Adding type annotations is going to increase how verbose your code is, especially if you move to Flow or TypeScript & especially when you consider generics. A language like PureScript can help, since the types and function definitions are separated so types are mixed in with function definitions and good type inference can reduce how much extra typing has to be done, but ultimately there is just more information and that’s going to take up more space.
  • 90. A N OT H E R B U I L D S T E P To be honest, this wasn’t really an issue for our team. We already knew we were going to have some kind of build step. Even if we went with no types, we’d still want to look at code splitting, minification, transpiliation. But if you’re not already building JS, this is something to consider.
  • 91. I N T E R O P One big question we had as a team was, ‘How does the work with other JS libraries’. TypeScript & Flow both handle this in a similar way:
  • 92. D E F I N I T I O N F I L E S Definition files. Basically, you can grab a module not written with Flow/TypeScript annotations, and separately pull down a file that defines the types used in that module. Definitions files are awesome, and super helpful, but they do come with a few problems
  • 93. P R O B L E M S W I T H D E F I N I T I O N S The first thing to keep in mind is that definition files aren’t authored by the same person as the module; it’s generally another developer lending a hand. Unfortunately, that means they might not stay in-sync with the module since they are developed separately. If you’re like we were early on, and always on the latest version of modules, you could find yourself out of sync pretty often. We solved that by simply locking down our dependencies to a specific known-good version; something we needed to do at some point in the project any way. Secondly, there is always the possibility that there is no definition file for the library you are using. In that case, there are two options. One is to just use the `any` type, which in my opinion is a non-starter. The other, which we've taken more than one, is to author your own definition file; which is what we did.
  • 94. A N Y Both Flow & TypeScript have a way to forego type checking: the `any` type. If you add the `any` annotation, no type checking takes place for that value/ function/whatever. It seems like a great idea for quick prototyping, or when you want to work with some library that doesn’t have a definition file written.
  • 95. A N Y I S L I T E R A L LY T H E WO R S T Let’s consider for a moment that we’ve already made the decision to add static types to our JavaScript, why would want to disable checking? We throw out all of the benefits we get from static typing.
  • 96. A N Y A N D I N F E R E N C E Flow and TypeScript both have the `any` type and they both have type inference, but how they handle `any` when inferring the types in your code is different.
  • 97. F LOW O N LY I N F E R S A N Y F O R L I B R A R I E S T H AT H AV E N O T Y P E S D E F I N E D Flow will not infer the `any` type for your code, that doesn’t stop you from explicitly adding `any` to something when the compiler can’t figure out the type. However, it will infer `any` for a third party library that has no accompanying type definitions.
  • 98. T Y P E S C R I P T W I L L H A P P I LY I N F E R A N Y Right out of the box, TypeScript will happily infer `any` when the compiler can’t figure out a more precise type. It does this instead of throwing error.
  • 99. YO U C A N & S H O U L D D I S A B L E T H AT Luckily, TypeScript comes with a way to opt-out of this in the form of a compiler option called `no-implicity-any`. So instead of choosing `any` the TypeScript compiler will give you an error; you can still be explicit and add `any`. Of course, you should never do that because any is literally the worst.
  • 100. W H Y S H O U L D A N Y B E AVO I D E D Let’s take a look at a more concrete example of why `any` should be avoided.
  • 101. f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … } Here’s a function that, more or less, comes from our project, and I’d like to use it to illustrate some of the problems with `any`. But first, a little detour, because this function uses an advanced feature of TypeScript/Flow called generics, which needs a little bit of explanation first.
  • 102. f u n c t i o n i d < T > ( a r g : T ) : T = > a r g ; Generics, this very simple case, are used to let the caller of the function determine either the argument type and/or its return type. In this example we define a generic identity function, it just returns whatever you send it. What we are saying is this `id` function works on any type `T`, called a type variable. Generics let us enforce that the return type & argument type are the same. The argument must be of type `T` and so must the return value `T`. Generics are a much more powerful, but that’s a topic for another time.
  • 103. f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … } Ok so back to our function
  • 104. f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … } We want to make a `Thing`, which is just an object that holds a value of type T.
  • 105. f u n c t i o n m a k e T h i n g < T > ( … a r g s : [ ] a n y ) : T h i n g < T > { … } And it does that by accepting any number of arguments of any type.
  • 106. W H AT ? What?
  • 107. H O W ? Like…how? No seriously, I want to know. How do you go from any number of arguments, of any type, to an object of a specific type? I’ll tell you
  • 108. YO U D O N ’ T You don’t. At least not without being implicit about what at least one of the arguments passed in is.
  • 109. T H AT T Y P E I S A L I E That type is a lie. I mean, it’s totally valid, don’t get me wrong…but it’s still lying to you. That type isn’t being honest about the arguments in expects. There are a bunch of ways to improve that type, the simplest, and the one that our team really needed, is
  • 110. f u n c t i o n m a k e T h i n g < T > ( a r g : T ) : T h i n g < T > { … } This one. All we really need to do was wrap a value of type T, in an object. And we want to ensure that the argument passed in is a `T` so we don’t wrap up something we don’t want.
  • 111. A N Y L I K E I N H E R I TA N C E & S T R I N G S H A S I T ’ S P L AC E Any, like inheritance and strings has it’s place. But when you use it, it greatly inhibits everything we hope to gain from static typing.
  • 112. CO N C L U S I O N Alright so let’s wrap this up. What do we get by adding types
  • 113. P R E C I S I O N Types allow us to be precise about our function inputs and outputs and define types that actually express what we want.
  • 114. I N VA R I A N T S By being explicit about our inputs and outputs, we can enforce invariants. If we supply the wrong types of values, our code simply won’t compile.
  • 115. T E S T R E D U C T I O N All of this helps reduce the number of tests we have to write, and allows us to focus on the tests we actually need to write.
  • 116. R E A S O N I N G Test reduction is a great benefit, but the real draw, in my opinion, is the understanding that comes when static types get added to the mix.
  • 117. O F CO U R S E T H E R E A R E T R A D E O F F S Of course, there are tradeoffs.
  • 118. R A M P U P T I M E Depending on experience level & background there is going to be some increased ramp-up time; especially if your team has never dealt with static type systems before.
  • 119. V E R B O S I T Y Adding static type information means adding more code, there’s no two ways about it. Type inference helps, but at the end of the day there is just more code to read and write.
  • 120. B U I L D I N G Honestly, I can’t think of any project that doesn’t already have a build step, but if you don’t, adding static types will make you add one.
  • 121. I N T E R O P For me, this is the one of the biggest tradeoffs and one we didn’t fully appreciate. If you’re using large, well-known libraries there is likely a typed version, or type definitions for it. But that isn’t always the case, especially for smaller libraries. Luckily, there are options. The one our team prefers is writing our own definitions for the bits we need. It takes extra time, but it’s far safer.
  • 122. A N Y Lastly, `any`. It disables type checking, and really cuts into the benefits that static types are meant to bring. If you’re using TypeScript, don’t let the compiler infer it. If you’re using Flow, add a definition file to any third-party lib you use. If you use Elm or PureScript…nevermind then; they don’t have this problem.
  • 123. I S I T WO R T H I T Is it worth it? Obviously I think so, right? Our team started using TypeScript toward the end of 2016 and we’ve gone through a lot of the pains outlined here. We’ve hit issues with ramp up time for new developers. We’ve had to get used to writing more verbose code and we’ve fought the urge to just at `any` whenever things got a little to annoying. And we did all of that because of what we gained by introducing types far outweighed any of the pain we had to go through. We have more confidence in what our code does, our tests focus more on the logic of our code than before, and developers that have never been in our code before can quickly figure out what is going on. All because we decided to add types.
  • 124. G I V E I T A S H OT If you haven’t already, you should give it a shot. Maybe going all the way to PureScript or Elm is going to work for you, it didn’t for us (despite my best efforts), so check out TypeScript or Flow. Find a small project to try it out on, or a big project like we did. Build a single piece of an existing project using types. Whatever. I think you’ll find that what you gain is well worth any trade-offs you have to make.
  • 125. T Y P E S C R I P T H T T P S : / / W W W.T Y P E S C R I P T L A N G .O R G F LOW H T T P S : / / F LO W.O R G / E L M H T T P : / / E L M - L A N G .O R G / P U R E S C R I P T H T T P : / / W W W. P U R E S C R I P T.O R G /
  • 126. T Y P E S H T T P S : / / W W W. D E S T R O YA L L S O F T WA R E .CO M / CO M P E N D I U M / T Y P E S T Y P E S A S D E S I G N TO O L S H T T P S : / / W W W.YO U T U B E .CO M / WATC H ? V = 6 M U AV D 6 I 4 O U W H Y U S E S TAT I C T Y P E S I N J AVA S C R I P T H T T P S : / / M E D I U M . F R E E CO D E C A M P.O R G / W H Y - U S E - S TAT I C -T Y P E S - I N - J AVA S C R I P T- PA R T- 1 - 8 3 8 2 DA 1 E 0 A D B Gary Bernhardt Kris Jenkins Preethi Kasireddy
  • 127. T H A N K YO U @ t h u n k _ l i f e Thank you very much.