Programming TypeScript Boris Cherny O’Reilly Media
25 notes/highlights
Created by Ahmed Mansour Ouda – Last synced September 21, 2022
3. All About Types
Note that while read-only arrays can make your code easier to reason about in some cases by avoiding mutability, they are backed by regular JavaScript arrays. That means even small updates to an array result in having to copy the original array first, which can hurt your application’s runtime performance if you’re not careful. For small arrays this overhead is rarely noticeable, but for bigger arrays, the overhead can become significant. September 14, 202244
Lee Byron’s excellent immutable September 14, 202245
JavaScript programmers usually use the two interchangeably, though there is a subtle semantic difference worth mentioning: undefined means that something hasn’t been defined yet, and null means an absence of a value (like if you tried to compute a value, but ran into an error along the way). These are just conventions and TypeScript doesn’t hold you to them, but it can be a useful distinction to make. September 14, 202245
TypeScript also has void and never . These are really specific, special-purpose types that draw even finer lines between the different kinds of things that don’t exist: void is the return type of a function that doesn’t explicitly return anything (for example, console.log ), and never is the type of a function that never returns at all (like a function that throws an exception, or one that runs forever) September 14, 202245
If unknown is the supertype of every other type, then never is the subtype of every other type. We call it a bottom type . That means it’s assignable to every other type, and a value of type never can be used anywhere safely. This has mostly theoretical significance,6 but is something that will come up when you talk about TypeScript with other language nerds. September 14, 202246
Types that mean an absence of something Type Meaning null Absence of a value undefined Variable that has not been assigned a value yet void Function that doesn’t have a return statement never Function that never returns September 14, 202246
Note By convention, enum names are uppercase and singular. Their keys are also uppercase. September 15, 202247
A const enum doesn’t let you do reverse lookups, and so behaves a lot like a regular JavaScript object. It also doesn’t generate any JavaScript code by default, and instead inlines the enum member’s value wherever it’s used (for example, TypeScript will replace every occurrence of Language.Spanish with its value, 1 ). TSC Flag: preserveConstEnums September 15, 202248
const enum inlining can lead to safety issues when you import a const enum from someone else’s TypeScript code: if the enum author updates their const enum after you’ve compiled your TypeScript code, then your version of the enum and their version might point to different values at runtime, and TypeScript will be none the wiser. September 15, 202249
Everything looks great— Chairs and Cups work exactly as you expect… until you realize that all numbers are also assignable to enums! That behavior is an unfortunate consequence of TypeScript’s assignability rules, and to fix it you have to be extra careful to only use string-valued enums: September 15, 202249
All it takes is one pesky numeric value in your enum to make the whole enum unsafe. Warning Because of all the pitfalls that come with using enums safely, I recommend you stay away from them—there are plenty of better ways to express yourself in TypeScript. And if a coworker insists on using enums and there’s nothing you can do to change their mind, be sure to ninja-merge a few TSLint rules while they’re out to warn about numeric values and non- const enums. September 15, 202250
Almost. When unknown is part of a union type, the result of the union will be unknown . You’ll read more about union types in “Union and intersection types”. September 15, 202251
Objects in JavaScript use strings for keys; arrays are special kinds of objects that use numerical keys. September 15, 202251
There’s one minor technical difference: {} lets you define whatever types you want for built-in methods on the Object prototype, like .toString and .hasOwnProperty (head over to MDN to learn more about prototypes), while Object enforces that the types you declare are assignable to those on Object ’s prototype. For examp September 15, 202251
The acronym DRY stands for “Don’t Repeat Yourself”—the idea that code shouldn’t be repetitive. It was introduced by Andrew Hunt and David Thomas in their book The Pragmatic Programmer: From Journeyman to Master (Addison-Wesley). September 15, 202251
The way to think about a bottom type is as a type that has no values. A bottom type corresponds to a mathematical proposition that’s always false. September 15, 202251
4. Functions
A quick refresher on terminology: A parameter is a piece of data that a function needs to run, declared as part of a function declaration. Also called a formal parameter . An argument is a piece of data that you passed to a function when invoking it. Also called an actual parameter . September 16, 202253
Sometimes, you might opt for a variadic function API—one that takes a variable number of arguments—instead of a fixed-arity API that takes a fixed number of arguments. Traditionally, that required using JavaScript’s magic arguments object. arguments is “magic” because your JavaScript runtime automatically defines it for you in functions, and assigns to it the list of arguments you passed to your function. Because arguments is only array-like, and not a true array, you first have to convert it to an array before you can call the built-in .reduce on it: September 16, 202254
apply binds a value to this within your function (in this example, we bind this to null ), and spreads its second argument over your function’s parameters. call does the same, but applies its arguments in order instead of spreading. bind() is similar, in that it binds a this -argument and a list of arguments to your function. The difference is that bind does not invoke your function; instead, it returns a new function that you can then invoke with () , .call , or .apply , passing more arguments in to be bound to the so far unbound parameters if you want. TSC Flag: strictBindCallApply To safely use .call , .apply , and .bind in your code, be sure to enable the strictBindCallApply option in your tsconfig.json (it’s automatically enabled if you already enabled strict mode). Typing this If you’re not coming from JavaScript, you may be surprised to learn that in JavaScript the this variable is defined for every function, not just for those functions that live as methods on classes. this has a different value depending on how you called your function, which can make it notoriously fragile and hard to reason about. September 18, 202256
Generator functions ( generators for short) are a convenient way to, well, generate a bunch of values. They give the generator’s consumer fine control over the pace at which values are produced. Because they’re lazy—that is, they only compute the next value when a consumer asks for it—they can do things that can be hard to do otherwise, like generate infinite lists. September 18, 202258
A rule of thumb is: if it’s valid JavaScript code, then it’s value-level; if it’s valid TypeScript but not valid JavaScript, then it’s type-level. September 18, 202262
Overloaded function A function with multiple call signatures. September 21, 202264
Overloads come up naturally in browser DOM APIs. The createElement DOM API, for example, is used to create a new HTML element. It takes a string corresponding to an HTML tag and returns a new HTML element of that tag’s type. TypeScript comes with built-in types for each HTML element. These include: HTMLAnchorElement for elements HTMLCanvasElement for elements HTMLTableElement for elements Overloaded call signatures are a natural way to model how createElement works. Think about how you might type createElement (try to answer this by yourself before you read on!). The answer: type CreateElement = { ( tag : ‘a’ ) : HTMLAnchorElement ( tag : ‘canvas’ ) : HTMLCanvasElement ( tag : ‘table’ ) : HTMLTableElement ( tag : string ) : HTMLElement } let createElement : CreateElement = ( tag : string ) : HTMLElement = > { // … } We overload on the parameter’s type, matching on it with string literal types. We add a catchall case: if the user passed a custom tag name, or a cutting-edge experimental tag name that hasn’t made its way into TypeScript’s built-in type declarations yet, we return a generic HTMLElement . Since TypeScript resolves overloads in the order they were declared,7 when you call createElement with a string that doesn’t have a specific overload defined (e.g., createElement(‘foo’) ), TypeScript will fall back to HTMLElement . To type the implementation’s parameter, we combine all the types that parameter might have in createElement ’s overload signatures, resulting in ‘a’ | ‘canvas’ | ‘table’ | string . Since the three string literal types are all subtypes of string , the type reduces to just string . September 21, 2022 67
Note In all of the examples in this section we overloaded function expressions. But what if we want to overload a function declaration? As always, TypeScript has your back, with an equivalent syntax for function declarations. Let’s rewrite our createElement overloads: function createElement ( tag : ‘a’ ) : HTMLAnchorElement function createElement ( tag : ‘canvas’ ) : HTMLCanvasElement function createElement ( tag : ‘table’ ) : HTMLTableElement function createElement ( tag : string ) : HTMLElement { // … } September 21, 202268
JavaScript functions are just callable objects September 21, 202268