Typed Ember extends Confidence
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 23 | |
Author | ||
License | CC Attribution 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor. | |
Identifiers | 10.5446/62161 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
EmberConf 20214 / 23
2
3
5
9
10
12
13
14
15
16
17
18
21
22
00:00
Data Encryption StandardDecision tree learningMultiplication signAuthorizationGame theoryComputer animationJSONMeeting/Interview
00:55
GenderConfidence intervalProgrammer (hardware)Computer animation
01:31
BuildingCodeComputer architectureMobile appVector potentialMetropolitan area networkDegree (graph theory)Demo (music)Software developerGoodness of fitBitData conversionSoftwareComputer scienceLocal ringInteractive televisionMeeting/InterviewComputer animation
02:55
Sign (mathematics)Type theoryPrimitive (album)Symbol tableInternetworkingObject (grammar)Limit (category theory)GEDCOMEmailPresentation of a groupRight angleSymbol tableNumberPrimitive (album)Type theoryVariable (mathematics)Formal languageConfidence intervalSocial classObject (grammar)Functional (mathematics)Instance (computer science)MappingSet (mathematics)ImplementationVariety (linguistics)Error messageResultantMobile appAreaDemo (music)Computer programmingBitDegree (graph theory)Multiplication signDisk read-and-write headTypprüfungRun time (program lifecycle phase)Software developerCodeCategory of beingString (computer science)Key (cryptography)Boolean algebraAdditionException handlingContext awarenessData storage deviceMeeting/InterviewComputer animation
07:38
Game theoryMessage passingInterface (computing)Shape (magazine)Type theoryOrdinary differential equationExecution unitWorld Wide Web ConsortiumBitType theoryCodeDifferent (Kate Ryan album)Pairwise comparisonFunctional (mathematics)NumberConfidence intervalSoftware bugDeclarative programmingRule of inferenceCASE <Informatik>Computer fileValidity (statistics)CompilerInformationString (computer science)Context awarenessoutputExtension (kinesiology)Error messageAdditionBounded variationCategory of beingPoisson-KlammerData structurePrimitive (album)AngleParameter (computer programming)Mobile appMultiplication signVector potentialOverhead (computing)Formal languageKey (cryptography)TypprüfungPhysical systemDatabaseSoftware developerObject (grammar)Complete metric spaceText editorCuboidInterface (computing)Theory of relativityFluid staticsVariable (mathematics)Computer animation
14:51
Inheritance (object-oriented programming)Data typeType theoryPlug-in (computing)Term (mathematics)Game controllerService (economics)MiniDiscClique-widthEndliche ModelltheorieGamma functionMaxima and minimaDew pointEmailMIDIUniform resource locatorMetreGame theoryData modelInheritance (object-oriented programming)Mobile appWebsiteElectronic program guideDefault (computer science)Installation artWindows RegistrySocial classScripting languageLine (geometry)Common Language InfrastructureError messageBoilerplate (text)String (computer science)Computer fileOrder (biology)Data storage deviceType theoryEndliche ModelltheorieStrategy gameMultiplication signModule (mathematics)Hacker (term)Projective planeDirectory serviceLibrary (computing)Repository (publishing)Utility softwareConfiguration spaceDeclarative programmingService (economics)CodeConfidence intervalLatent heatHookingKey (cryptography)Functional (mathematics)Plug-in (computing)OvalCategory of beingAttribute grammarInitial value problemCASE <Informatik>Data typeSinc functionPower (physics)Asynchronous Transfer ModeRoutingTrailSubject indexingMessage passingElectronic mailing listMereologyData conversionMixed realityInterior (topology)ChainClassical physicsConstructor (object-oriented programming)Software bugPoint (geometry)Spectrum (functional analysis)QuicksortBitDirection (geometry)CompilerExistenceCommitment schemeComputer clusterConnectivity (graph theory)Hidden Markov modelComputer animationSource code
24:35
Inheritance (object-oriented programming)NumberComponent-based software engineeringIcosahedronGamma functionWorld Wide Web ConsortiumGame theoryData modelType theoryEuclidean vectorConnectivity (graph theory)Electronic mailing listMobile appQuery languageMultiplication signCompilation albumWave packetProjective planeConfidence intervalOpen setType theoryObject (grammar)CodeGroup actionSoftware developerFunctional (mathematics)Category of beingResultantParameter (computer programming)Line (geometry)Condition numberMereologyError messageMessage passingEndliche ModelltheorieInterface (computing)Open sourceQuicksortComputer fileMilitary baseBlogCompilerComplete metric spaceOverhead (computing)Product (business)CASE <Informatik>String (computer science)CuboidLevel (video gaming)Reverse engineeringElectronic visual displayCommitment schemeTemplate (C++)RoutingSubject indexingComputer configurationGeneric programmingProxy serverFilter <Stochastik>Software bugCommon Language InfrastructureUtility softwareDifferent (Kate Ryan album)Physical systemScripting languageText editorNumberCodeLink (knot theory)Computer animation
32:15
JSON
Transcript: English(auto-generated)
00:20
The prize-winning author Maya Angelou once said after publishing her 11th book that every time she wrote another one she'd think to herself uh-oh they're gonna find out now I've run a game on everybody and they're gonna find me out. Even Maya Angelou experienced imposter syndrome but to call it a
00:42
syndrome is to downplay how universal it is. To that end the psychologists who first described this experience didn't call it imposter syndrome they called it imposter phenomenon. You will find imposters in all facets of society regardless of culture, gender, age, or occupation. In
01:04
fact psychologists estimate that 70% of people will experience this feeling at least once in their lives and you can be sure that 70% of people are not incompetent. So it's not an issue of competence it's an issue of confidence. I
01:23
am one of these imposters. I'm a programmer from a non-traditional background. My first career was as an architectural designer. I designed buildings and urban landscapes for tech companies in Silicon Valley. You might even recognize my work. When I left architecture I attended a code school
01:43
here in Portland where I learned Ember among other things. It was awesome. I was so stoked to get my new career started. One evening I recruited some code school colleagues to join me at the local JavaScript meetup. The meetup turned out to be a bit of a dud but by stroke of good fortune in walked in
02:02
a crowd of people all clad in Ember gear. It turned out that an EmberConf happy hour had arrived. Despite being super nervous I did my best networking and met a crowd of very nice folks but one conversation stuck with me the most. After bragging about having graduated with his computer science
02:23
degree the year I was born a man tried to convince me that code school is a bad idea because the market was oversaturated with junior developers. He then proceeded to give my male code school colleagues sage career advice. I started to think maybe I don't belong here. Later at our schools showcase
02:47
demo day I presented my hobby app to potential employer after potential employer but one interaction in particular stuck out. I recognized Yehuda Katz from across the room. He's like the guy that wrote the thing right? I
03:03
stumbled through my presentation and less than an hour later I had a very flattering email in my inbox and I've been working at Tilda ever since. And yet I still felt like an imposter. Mr. CS degree was still in my head. Three years into my journey at Tilda we decided to finish converting our app to
03:24
Octane. Since we were already refactoring our entire app we figured we might as well convert it to TypeScript at the same time. Godfrey gave us a TypeScript demo and we were off. At first I felt a little
03:41
overwhelmed but eventually as we worked our way through the app I started to really see the benefits of TypeScript. I found myself drawn to working on more complex sometimes crufty areas of our code base. Code that had intimidated me before. Beyond transforming our code base I myself was starting to feel
04:02
transformed from a competent but hesitant mid-level engineer to a competent and confident senior engineer. I was starting to feel less like an imposter. Imposter phenomenon is talked about quite a bit in the programming community and there's lots of wonderful advice to be found.
04:22
Everything from surround yourself with supportive people to just decide to be confident. Rarely are technical and tooling solutions offered. Based on my experience I propose adding one more item here. Convert your app to TypeScript to foster confidence in your engineering team. Now the last thing I
04:43
want is to imply that converting your app to TypeScript is going to be a piece of cake and if you don't agree with me you must be well an imposter. So we're going to start with the basics. What even is a type? What is TypeScript? Then we'll look at what TypeScript looks like in an Ember app before circling back to the benefits of TypeScript in the context of developer
05:03
confidence. You've likely come across that concept of types before. A values type tells us what type of data it stores and what you can and cannot do with that data. There are nine types in JavaScript. The most basic types are
05:21
primitives. JavaScript's primitive types include number, string, boolean, bigint, symbol, undefined, and null. You can check a value's primitive type by using typeof with the exception of null. In addition to primitives JavaScript has
05:45
more complex structural types. An object is a mutable collection of properties organized in key-value pairs. Arrays, sets, maps, and other class instances are all objects under the hood. Now because typeof will return the string object
06:05
regardless of the class, instanceof and other checks are more useful here. The other structural type is function. A function is a callable object. You may
06:25
have heard that JavaScript is a loosely typed and dynamic language. Loosely typed means that every variable has a type but you cannot guarantee that the type will stay the same. For example you can change the
06:42
type of a variable by assigning a different value to it. Even stranger in some instances JavaScript will implicitly coerce your value to a different type, sometimes in unexpected ways. For example you can add the number two to the number two as expected but JavaScript will also allow you to add
07:04
any of the other JavaScript types to the number two without throwing an error leading to a variety of not so useful results. JavaScript is also a dynamically typed language. This means that you never need to specify the type of your variable. Instead the JavaScript implementation determines the type at
07:25
runtime and it will do the best it can with that knowledge at runtime. Sometimes that means coercion as we just saw and sometimes you get the dreaded type error. What could go wrong? It turns out quite a bit. In 2018 the
07:45
error monitoring company Rollbar analyzed their database of JavaScript errors and found that eight of the top ten were some variation of trying to read or write to a null or undefined value. Furthermore because of type coercion it's possible you might have additional type related bugs that don't
08:02
even cause errors to be thrown in your app. All of this leads to uncertainty and uncertainty is the enemy of confidence. Fortunately TypeScript is here to help. TypeScript is an extension of JavaScript. When writing TypeScript you can use all of JavaScript's features plus additional
08:24
TypeScript features. The main difference syntactically is that TypeScript adds type annotations on top of the JavaScript you already know and love. When the compiler turns your TypeScript into JavaScript it determines the types of the values in your code and checks the validity of
08:43
those types in the context in which you use them. Before outputting standard JavaScript with the type information removed if you've used a value incorrectly you get a type checking error alerting you to the issue before
09:00
you ship your code. Also because our text editor can run the TypeScript compiler in the background it can integrate the type information and other related documentation into the editor user experience to give you for example code completion, hover info, and error messages. VS code gives you these
09:29
features out of the box no installation required and because TypeScript comes with all of JavaScript's built-in types baked in such as DOM types you have a ton of information at your fingertips.
09:43
It's basically like having an answer key to your code. Having an answer key available to you at all times not only reduces your cognitive overhead but it makes you feel like a rock star and it's hard to feel like an imposter when you feel like a rock star. So how does TypeScript get the answer key?
10:02
Because TypeScript is a strictly and statically typed language. Statically typed means that TypeScript checks your code for potential type errors at compile time. For all of this magic to work TypeScript needs to know the type of
10:20
your values that can compile time. Sometimes TypeScript can infer the value of a type from its usage. Other times you may need or prefer to declare the value to the type of the value with a type annotation. It's worth noting that
10:41
unlike some other statically typed languages TypeScript will still compile even when you have type checking errors. In other languages the type system can feel like a gatekeeper. In TypeScript it feels more like a messenger. While type checking errors can be frustrating at times they're almost always telling you useful information. TypeScript is also more
11:07
strictly typed than JavaScript. Once a variable has a type other than undefined or null, TypeScript will not allow you to change that type. Also TypeScript disallows a lot of implicit type coercion. For example you can add the
11:23
number two to the number two but you will get a type checking error if you try to add for example an array or a set to the number two. Interestingly TypeScript will still allow you to add a string to a number. The compiled JavaScript will implicitly coerce the number to a string before
11:40
concatenating the two strings. In my example this might seem a little ridiculous though you might be able to imagine it happening in the case of say an input value. In the real world however adding a number to a string is a common enough thing to do intentionally that it's considered idiomatic. The TypeScript team decided not to make TypeScript too strict in
12:04
this case. If you disagree with them you can enable an ESLint rule to forbid this. This is one example of how TypeScript's strictness is configurable. You can also increase or decrease the strictness by a file called tsconfig. Strictness helps you to achieve something called type safety.
12:27
This means that TypeScript will help you avoid those pesky type errors. In fact researchers found that TypeScript detected 15% of public bugs. Shipping fewer bugs helps you to become a more confident developer.
12:43
TypeScript has all of JavaScript types. Here are examples of explicit type declarations for each of JavaScript's primitives represented in TypeScript. Because TypeScript can infer the type of your value from its usage these explicit annotations may not always be necessary. The annotations for
13:05
structural types however start to get a little more complicated. The array type is an example of a generic type. A reusable type that takes another type as an argument denoted with angle brackets. In this case the array type
13:21
takes the string type as an argument and TypeScript knows that our variable is an array of strings. To declare the type of a function declare the type of each variable and the return type as so. For an object use an interface to
13:45
represent each of the properties and their types. In addition to JavaScript's basic types, TypeScript provides even more types. Let's go over a few types you might need to understand this talk. The void type is the absence of
14:05
a type. The void type is most commonly used to specify that we don't expect this function to return anything. The unknown type is useful for when you don't know the type of a value. When you use unknown you can narrow the type
14:21
of the value using type of or other comparisons. The any type can also be used when you don't know the type. The difference though is that when you annotate a value as any, TypeScript will allow you to do anything with it. Essentially when you use the any type you are opting out of static type
14:43
checking for that variable. Proceed with extreme caution. Alright let's convert an app to TypeScript. We'll use the super rentals app from the Ember guides as our example. Super Rentals is a website for browsing interesting places to stay during your post COVID vacation. Super Rentals is a
15:05
very modern Ember app using the latest and greatest Ember Octane features. Admittedly using TypeScript with pre-Octane Ember was clunky. With Octane's native classes however using TypeScript with Ember is pretty straightforward. The first step is to run Ember install Ember CLI TypeScript.
15:28
Installing this package adds everything you need to compile TypeScript. The TypeScript package itself, types packages for each of Ember's modules, a default TS config, and some utility types and directories. While Ember itself
15:46
isn't fully written in TypeScript yet, there's a project called Definitely Typed that acts as a repository for types for hundreds of libraries including Ember. You can install these types as packages then import them the same way you would a JavaScript module. Now that we've installed TypeScript we
16:06
can start converting files. Fortunately TypeScript allows for gradual typing. This means that you can use TypeScript and JavaScript files interchangeably so you can convert your app piecemeal. Of course many of your files might reference types in other files that haven't been converted yet.
16:24
There are several strategies you can employ to avoid a chain reaction resulting in having to convert your entire app at once. TypeScript declaration files are a way to document TypeScript types for JavaScript files without converting them. Alternatively you can sometimes get
16:42
pretty far just by annotating types as unknown or you can opt out of type checking for a value by annotating it as any. A better strategy than any however is to mark offending parts of your code with the TS expect error directive. This comment will ignore a type checking error and allow TypeScript
17:02
compiler to assume that the value of the type is any. The value is of the type any, sorry. If the code stops triggering the error TypeScript will let you know. You can also gradually increase TypeScript's strictness. There are two ends of the spectrum here. On one end start with all of the checks
17:23
disabled then enable them gradually as you start to feel more comfortable with your TypeScript conversion. I do recommend switching to strict mode as soon as possible because strictness is sort of the point of TypeScript to
17:40
avoid shipping detectable bugs in your code. On the other end start in strict mode. This is the strategy we will use for converting super rentals since I want you to see the full power of TypeScript. In fact I want my TypeScript even stricter. I'm going to also add the TypeScript ESLint plugin which
18:01
adds additional checks. So which file should we convert first? Here are four strategies to choose from. One, outer leaves first. Models likely have the fewest non ember imports so you won't have to use as many of our gradual
18:21
typing hacks. This strategy is best if your app already uses Octane since Octane getters might not always be compatible with computed properties. Two, inner leaves first. This strategy is best if you are converting to Octane simultaneously with TypeScript. You will need to make heavy use of our
18:41
gradual typing hacks. Three, you touch it you convert it. Whenever you are about to touch a file convert it to TypeScript first. This strategy is best if you don't have time to convert everything at once. And four, most fun first. Pick the files you are most curious about. Refactoring to TypeScript
19:03
is an awesome way to build confidence in your understanding of a chunk of code. This strategy is also great for onboarding new team members. The Tilda team tried all of these strategies for our elderly half classic half Octane app and settled on a mix of you touch it you convert it and most fun first. For
19:23
our super rentals conversion however we're going to approach the conversion outer leaves first. Our outermost leaf is the rental model and JavaScript it looks like this. The rental model keeps track of various
19:40
attributes about our vacation rentals. It also has a getter to categorize the type of the rental into either community or standalone. Step one, rename the file to TypeScript. And we're done! Congratulations you've just written your first TypeScript class. Because all valid JavaScript is valid TypeScript any
20:03
JavaScript code will still compile as TypeScript code. But we've got some type checking errors it looks like we have a little bit more work to do. These squiggly lines indicate type checking errors. Let's start from the top. Member title implicitly has an any type. This error is telling us that we
20:25
need to annotate the title attribute with the type. We can look at the seed data from the super rentals app to figure out what the type should be. It looks like the title is a string. Let's add the type annotation and we get a
20:42
new error. Property title has no initializer and is not definitely assigned in the constructor. This message is a little confusing but here's what it means. TypeScript expects properties to either be declared with an initial value, be set in the constructor, or be allowed to be
21:04
undefined. TypeScript doesn't really know that the adder decorator is making the property exist. So TypeScript thinks that this property will never get set. In this case we can tell TypeScript something external is setting this property by marking the value with the declare
21:21
property modifier. Let's go ahead and resolve the rest of the squiggly lines on the attributes. The last error is coming from ESLint asking us to
21:41
provide a return type for the type getter. Because we know that the type getter will always return either the string community or the string standalone, we can put string in as the return type or we can be extra specific and use a union of literal types for the return value. All right
22:02
we're free of squiggly lines let's commit. One more thing about models before we move on. The rental model doesn't have any relationships on it but if it did we would use a similar strategy to what we did with attributes the declare property modifier. Also the ember data types give us handy types
22:21
that keep track of the many intricacies of ember data relationships. Cool. The next leaf in includes routes. Let's convert the index route it's pretty simple with a model hook that accesses the ember data store and finds all of the rentals. First we'll rename the file to typescript and once again we
22:45
have some errors. The first error is member store implicitly has in any type. We know that the type of the store service is a store. We can import the store type from ember data and add the type annotation and because the
23:02
store is set by the service decorator we need to use the declare property modifier again. And the last squiggly line is again the linter telling us that we need a return type on the function. But how do you know what that return type is? Here's a little hack you can use to check the return type. Pop void
23:21
in as the return type. This gives us a type checking error as expected because we know the model hook does not actually return nothing. Hmm Promise array makes sense but I wouldn't expect an array of any values. It should be a more specific type. Something seems wrong here. We've run into one of the
23:44
first gotchas of using typescript with ember. Ember makes heavy use of string key lookups. For example here we look up all of the rentals by passing the rental string to the store's find all method. In order for typescript to know that the rental string correlates with the rental
24:01
model we need to add some boilerplate to the end of the rental model file. The ember cli typescript installation added a model registry for this purpose and we just need to register our rental model with the registry and now we get a much more useful error. It looks like our return
24:21
type is a promise array of rental models. We can add the appropriate imports and the type annotation and now we have no more squiggly lines. Let's commit. Next let's try converting a component the innermost leaf of our app. The rentals filter component lists filters the list of
24:45
vacation rentals based on a past in search query. When we rename the file we see some squigglies. The first squiggly is the splinter reminding us to add a return type to the function. From reading the code it looks like we are expecting results to turn an array of rental
25:03
models. So let's put that for now. The next squiggly says property rentals does not exist on type empty interface. We are destructuring the args but it looks like typescript has no idea what properties the args
25:23
object should have. We need to tell typescript what the component arguments are. Fortunately the glimmer component type is a generic. It takes an optional type argument where you can specify your args like this. Now typescript knows about our components arguments but it's
25:47
complaining that the rentals argument type is unknown. By doing a little sleuthing tracing the component invocations back to the route template we discover that the rentals argument is the resolved model from the index
26:02
route. We can extract the model type from the index route by using the model from utility type that we can borrow from the ember CLI typescript documentation cookbook. And now we have some new squiggly lines. This lengthy error is telling us that the rentals argument returns an array
26:24
proxy of rental models but filters coercing it into an array of rental models which has slightly different behavior. For example array proxy doesn't have push or pop methods like an array does. This could cause a bug in our future. We always want to return an array so we can resolve this by first
26:46
converting the rentals argument to an array before using it. Bye bug. The last error is argument of type unknown is not assignable to parameter of type
27:00
string. Typescript is telling us that the includes method on the rental title string expects a string to be passed to it but we've passed an unknown. Let's find out what that query argument type actually is. Just like with rentals we want to extract the type from the calling component if possible. In this
27:21
case query is a tracked property on the rentals component. We can get the type of that property by importing the rentals component type and looking up the type of the query property using a similar syntax to what we'd use to access a value on an object. Phew we're done. Let's commit. Let's take a look at
27:42
one more component. The map component displays a map of the given coordinates retrieved from the map box API. First we'll rename the file to typescript and we see some errors. Let's start by adding our arguments interface and resolving the return type lens. And look at that all of our red
28:04
squigglies went away. For your first pass converting your app I think it's totally fine to merge the unknown types like this but I have a few more things I want to show you so we'll add the real types now. We'll reverse engineer the types from one of the invocations. With the actual types we
28:22
still don't have any type checking errors all good but I wonder if there's anything we can do to make this component easier to reuse. For example is there a way to throw an error anytime an engineer forgets to pass in one of these arguments? It turns out there is. We can use a union type to tell
28:42
typescript that the longitude and latitude arguments might be undefined. Then we can use getters to alias the arguments but with an added check using ember-debugs-assert. The assertion will throw an error with the provided message if the condition is false. Also the type for assert is
29:02
written such that typescript now knows that the condition must be true for all of the following lines. This allows us to drop the undefined type from the argument before returning it as a number. Thus we can use this dot longitude and this dot latitude elsewhere in our code without having to worry about the possibility of them being undefined. Also the best part is that the
29:24
assert code and its condition are stripped from your production builds so you haven't added any production overhead. Alright let's commit and we're done converting our app. If you don't want to switch to typescript you can
29:41
get some of typescript's benefits such as code completion and documentation on hover by using JSDOC documentation in your JavaScript along with the VS code text editor. JSDOC allows you to document types though it doesn't have all of typescript's features. VS code's JavaScript features are powered by the
30:00
typescript compiler under the hood so you even get access to typescript's built-in types. You can also add types packages from definitely types and VS code will use those types as well. Once you've documented the types in your JavaScript files you can even add a TS check comment to the top of your file to get type checking in your JavaScript files powered by typescript.
30:22
If you google why use typescript you can find all sorts of blogs about the technical benefits of typescript and sure there are many but to me where typescript really shines is not its technical but its personal benefits.
30:42
Many legacy code bases have code that people are scared to work on. I found that refactoring to typescript makes understanding these crafty spots so much easier and sometimes even fun. Once you've added types to a significant chunk of your project you will really start to see the benefits.
31:02
Type annotations coupled with JSDOC are a place to pool the knowledge of every engineer that ever worked on that code. Eventually you start to notice that you don't have to refresh your development app so many times to experiment because your code just works the first time around. I
31:20
used to be scared to open pull requests on open source projects because it felt too public and unsafe. Opening PRs on the Ember types on definitely typed was a great way to get started in open source and I've since moved on to opening PRs on other projects too. I've been to rest comp like four times and
31:41
each time I take the beginner and intermediate trainings and still feel totally overwhelmed. After using typescript for only a few months I was able to transition to writing rest and it made so much more sense. I will forever refer to typescript as baby's first type system. Typescript's answer
32:01
key gives me confidence so much so that I signed up to do this talk after years of saying maybe one day and now you all have to listen to me and for that I thank you.