TypeScript — Wrapping Up the Frontend Masters Tutorial
Generics, Type Parameter Constraints, Dictionaries in TS, etc.
Generics
Reusable components are incredibly important when programming. Creating a component that can work over a variety of types rather than a single one is key. Generics allows users to consume these components and use their own types.² Generics parameterize types the way that functions parameterize values. Types are constraints and the represent contracts between various entities.¹
With Generics, we get what is known as the type variable, a special kind of variable that works on types rather than values. We provide a type as a parameter to the dynamic function that we are building. For example:
Below, you’ll see you can name the type value anything you want. This value is local to this function only. It’s scope is only in this function, and the type value is a local variable. The name only has meaning within/to this function. The convention is that it is capitalized.
When we want to specify a default value of type, we would write the function like this:
Type Parameter Constraints
Constraints spell out the minimum requirement that your type must meet. With constraints, a generic function will no longer work over any and all types:
Generics are great for relating to things. Example: I take in an array of T and I give you back a dictionary of T
(((*ask for only what you need and return everything you can)))
Building and Mapping over a Dictionary:
Top and Bottom Types: Any and Never
Top Types: Any & Unknown
Top types are types that can hold any value. Top types are Any and Unknown. Unknown can receive any value, but you can not use unknown directly. It has to be narrowed in some way before use. Narrowing happens through type guards.
Any is great for the areas of our programs where we need to maintain flexibility. Unknowns are good for values that private, etc. They can still hold any value, but again, need the type guard to use the type.
Built in type guards: using logic checks with ‘typeof’ and ‘instanceof’. Or can define own type guards, user defined type guards.
Branded Types
A structural type-system uses the structure of the type, that is the name and type of the properties, to assert equivalence between types. This is the type-system that TypeScript is built on.³. If two type objects have different names, but the exact same attributes with same type values, TypeScript doesn’t think there is any difference between the two types.
To a nominally-typed system even if they share all the same properties, the two objects are are different;. By using the name of the type, Nominally-typed systems determine if the types are the same.
TypeScript was not built to support nominal types. Branding allows for simulating nominal types within TypeScript. A brand, or tag, is usually a string literal type added to a non-existent property on an object.³ The biggest downside of branding is that it introduces a fake property to type objects that does not exist at run time.
Another example of branding is a great example from a Kevin Greene blog:
Brand is a generic type with two type parameters. The first type parameter “K” represents our base type and the second type parameter “T” represents out tag or brand. The resulting type is an intersection of the base type and an interface with a “__brand” property, a private property to that brand. The “__brand” property is typed to our tag type, making the resulting branded type unique from its base type.⁴
Let’s say we’re working with multiple currencies:
Bottom Type: Never
Built-In Utility Types:
* Partial makes everything on a type optional:
type MayHaveEmail = Partial<HasEmail>;
const me: MayHaveEmail — {}; // everything is optional
*Pick allows us to select one or more properties of so
Below we are picking off of an object type by property key:
type PickA = Pick<{a: 1, b: 2}, “b” | “a”>;
const y: PickA;
y. //==> a or b are present here to chose from
*Extract allows us to select one or more properties of so
When you have a big intersection type, lets you select things that are assignable to a particular type. (ex: give me only strings, or only numbers)
In TypeScript, identifiers are ‘things that you can export’¹ (internally TypeScript calls them symbols). Identifiers can be associated with us to 3 things: value, type, namespace(like an object has type and has value).
Functions and variables are purely values. Extract value using ‘typeof’.
Interfaces are only types.
Classes are both types and values. Classes are factories to produces instances.
namespace AddressBook {
export class ABContact extends Contact {} // inner class
}
- TypeTypeScript 3 Fundamentals, v2 | Frontend Masters | “https://frontendmasters.com/courses/typescript-v2/”
- Generics | TypeScript | “https://www.typescriptlang.org/docs/handbook/2/generics.html”
- An Introduction to Nominal TypeScript | Andy Patterson | “https://betterprogramming.pub/nominal-typescript-eee36e9432d2”