 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 going to find out now I've run a game on everybody and they're going to find me out. Even Maya Angelou experienced imposter syndrome. But to call it a 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 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 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 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 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 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 school's showcase 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 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 Octane. Since we were already refactoring our entire app, we figured we might as well convert it to TypeScript at the same time. God Free gave us a TypeScript demo and we were off. At first I felt a little 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 crafty areas of our code base, code that had intimidated me before. On transforming our code base, I myself was starting to feel 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 a lot of wonderful advice to be found. 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 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 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 called primitives. Most primitive types include number, string, boolean, big int, symbol, undefined, and null. You can check a value's primitive type by using type of, with the exception of null. In addition to primitives, JavaScript has 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 type of will return the string object regardless of the class, instance of and other checks are more useful here. The other structural type is function. A function is a callable object. You may 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 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 2 to the number 2 as expected, but JavaScript will also allow you to add any of the other JavaScript types to the number 2 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 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 error monitoring company Rollbar analyzed their database of JavaScript errors and found that 8 of the top 10 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 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 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 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 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 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. 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? 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 your values at 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 the type annotation. It's worth noting that 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 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 number 2 to the number 2, but you will get a type checking error if you try to add, for example, an array or a set to the number 2. 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 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 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. 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. 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 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 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 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 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 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 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 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. 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 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 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. 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 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 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 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 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 adds additional checks. So, which file should we convert first? Here are four strategies to choose from. 1. Outer leaves first. Models likely have the fewest non-ember imports, so you won't have to use as many of our gradual typing hacks. This strategy is best if your app already uses Octane, since Octane getters might not always be compatible with computed properties. 2. 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 gradual typing hacks. 3. 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. 4. Most fun first. Pick the files you are most curious about. Refactoring to TypeScript 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 our super-rentals conversion, however, we are going to approach the conversion Outer Leaves First. Our Outer List Most Leaf is the rental model, and JavaScript, it looks like this. The rental model keeps track of various attributes about our vacation rentals. It also has a getter to categorize the type of the rental into either community or standalone. Step 1. 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 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 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 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 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 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 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, 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 that keep track of the many intricacies of ember data relationships. Cool. The next sleeve 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 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 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 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. 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 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 model, we need to add some boiler plate 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 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 list filters the list of vacation rentals based on a past in search query. When we rename the file, we see some squigglies. The first squiggly is the linter 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 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 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 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 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 proxy of rental models, but filter is coercing it into an array of rental models, which has slightly different behavior. For example, array proxy doesn't have pusher 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 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 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 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 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 lengths. And look at that. All of our red 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 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 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 Debug's assert. The assertion will throw an error with a provided message if the condition is false. Also, the type for assert is 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 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 get some of typescript's benefits, such as code completion and documentation on Hover, by using JS doc documentation in your JavaScript along with the VS code text editor. JS doc allows you to document types, though it doesn't have all of typescript's features. VS code's JavaScript features are powered by the 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. 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. 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. Type annotations coupled with JS doc 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 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 a REST conf like four times and 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 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.