|
| 1 | +# Advanced Types |
| 2 | + |
| 3 | +We have learned quite a bit about Types so far, but when it comes to large-scale projects there are more advanced types that help us write more robust code which will dive into now. The objectives of this lesson are: |
| 4 | + |
| 5 | +1. Viewing practical usage examples of advanced Types |
| 6 | +2. Understanding when to use advanced Types |
| 7 | + |
| 8 | +## Interfaces |
| 9 | + |
| 10 | +An interface declaration is another way to name an object type. Interfaces can be used interchangeably with type aliases and simple object types. |
| 11 | + |
| 12 | +```ts |
| 13 | +interface Point { |
| 14 | + x: number; |
| 15 | + y: number; |
| 16 | +} |
| 17 | + |
| 18 | +function printCoord(pt: Point) { |
| 19 | + console.log("The coordinate's x value is " + pt.x); |
| 20 | + console.log("The coordinate's y value is " + pt.y); |
| 21 | +} |
| 22 | + |
| 23 | +printCoord({ x: 100, y: 100 }); |
| 24 | +``` |
| 25 | + |
| 26 | +Like when we used a type alias above, the example works just as if we had used an anonymous object type. TypeScript is only concerned with the structure of the value we passed to `printCoord` - it only cares that it has the expected properties. |
| 27 | + |
| 28 | +You can also define properties in an interface to be optional using `?` or non-editable by specifying `readonly`. |
| 29 | + |
| 30 | +```ts |
| 31 | +interface UserInterface { |
| 32 | + readonly id: number; |
| 33 | + name: string; |
| 34 | + age?: number; |
| 35 | +} |
| 36 | + |
| 37 | +const user1: UserInterface = { |
| 38 | + id: 1, |
| 39 | + name: "John", |
| 40 | +}; |
| 41 | +``` |
| 42 | + |
| 43 | +Interfaces can be used with functions as well. |
| 44 | + |
| 45 | +```ts |
| 46 | +interface MathFunc { |
| 47 | + (x: number, y: number): number; |
| 48 | +} |
| 49 | + |
| 50 | +const add: MathFunc = (x: number, y: number): number => x + y; |
| 51 | +const sub: MathFunc = (x: number, y: number): number => x - y; |
| 52 | +``` |
| 53 | + |
| 54 | +## Classes |
| 55 | + |
| 56 | +TypeScript offers full support for the class keyword introduced in ES2015. As with other JavaScript language features, TypeScript adds type annotations and other syntax features to allow you to express relationships between classes and other types. Just like any other OOP language, classes have members or fields and a constructor function is used to instantiate the objects of this class with initial field values. Classes can also have member functions. |
| 57 | + |
| 58 | +```ts |
| 59 | +// Class with constructor |
| 60 | +class Person { |
| 61 | + id: number; |
| 62 | + name: string; |
| 63 | + |
| 64 | + constructor(id: number, name: string) { |
| 65 | + this.id = id; |
| 66 | + this.name = name; |
| 67 | + } |
| 68 | + |
| 69 | + greet(message: string) { |
| 70 | + console.log(message + this.name); |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +const alice = new Person(1, "Alice Green"); |
| 75 | +alice.greet("Hello"); |
| 76 | +``` |
| 77 | + |
| 78 | +You can use an `implements` clause to check that a class satisfies a particular interface. |
| 79 | + |
| 80 | +```ts |
| 81 | +interface PersonInterface { |
| 82 | + id: number; |
| 83 | + name: string; |
| 84 | + register(): string; |
| 85 | +} |
| 86 | + |
| 87 | +class Person implements PersonInterface { |
| 88 | + id: number; |
| 89 | + name: string; |
| 90 | + |
| 91 | + constructor(id: number, name: string) { |
| 92 | + this.id = id; |
| 93 | + this.name = name; |
| 94 | + } |
| 95 | + |
| 96 | + register() { |
| 97 | + console.log(`${this.name} is now registered`); |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +const alice = new Person(1, "Alice Green"); |
| 102 | +const mike = new Person(2, "Mike Jordan"); |
| 103 | + |
| 104 | +alice.register(); |
| 105 | +mike.register(); |
| 106 | +``` |
| 107 | + |
| 108 | +Classes may extend from a base class. A derived class has all the properties and methods of its base class, and also defines additional members. |
| 109 | + |
| 110 | +```ts |
| 111 | +class Employee extends Person { |
| 112 | + position: string; // additional member of this subclass |
| 113 | + |
| 114 | + constructor(id: number, name: string, position: string) { |
| 115 | + super(id, name); |
| 116 | + this.position = position; |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +const emp = new Employee(3, "Shawn", "Developer"); |
| 121 | +emp.register(); |
| 122 | +``` |
| 123 | + |
| 124 | +A field of a class can be one of the following: `public`, `private`, `protected`, or |
| 125 | +`readonly`. |
| 126 | + |
| 127 | +- `public`: This field can be read to and written as normal. |
| 128 | +- `private`: Only accessible by the instance of the class. For example, this variable cannot be accessed by any code other than the functions in the class. |
| 129 | +- `protected`: Similar to `private`, but subclasses can access the field. |
| 130 | +- `readonly`: This field can only be read, but it can never be assigned or have its value changed (similar to `const`). |
| 131 | + |
| 132 | +By default, all class members are `public` but you can apply other data modifiers like `private` and `protected` to control member visibility. Protected members are only visible to subclasses of the class they're declared in. Private is like Protected but doesn't allow access to the member even from subclasses. |
| 133 | + |
| 134 | +In general, it is considered good practice in coding to use the minimal visibility required. For example, if you never plan to write to the field, you should start with `readonly`. If you find that you need to write to the field, but only in the code in the class, you should use `private`. Usage of `protected` is rare. In most cases, well-structured code will have most instance variables of a class as `private`. |
| 135 | + |
| 136 | +We've tried to cover the basics of Classes here. You can read a lot more in detail [on the TypeScript handbook](https://www.typescriptlang.org/docs/handbook/2/classes.html). |
| 137 | + |
| 138 | +## Generics |
| 139 | + |
| 140 | +Generics allow us to create reusable and flexible components which can work over a variety of types rather than a single one. This allows users to consume these components and use their types. |
| 141 | + |
| 142 | +```ts |
| 143 | +// Without generics |
| 144 | +function getArray(items: any[]): any[] { |
| 145 | + return new Array().concat(items); |
| 146 | +} |
| 147 | + |
| 148 | +let numArray = getArray([1, 2, 3, 4]); |
| 149 | +let strArray = getArray(["alice", "dave", "mike"]); |
| 150 | + |
| 151 | +// With generics |
| 152 | +function getArray<T>(items: T[]): T[] { |
| 153 | + return new Array().concat(items); |
| 154 | +} |
| 155 | + |
| 156 | +let numArray = getArray<number>([1, 2, 3, 4]); |
| 157 | +let strArray = getArray<string>(["alice", "dave", "mike"]); |
| 158 | +``` |
| 159 | + |
| 160 | +The drawback of using `any[]` is that something like `numArray.push('hello')` would not throw an error, but when you use generics it acts as a placeholder with types defined and applied when creating variables from the generic. |
| 161 | + |
| 162 | +You can read more about generics and considerations for using this feature of TypeScript on [The TypeScript handbook](https://www.typescriptlang.org/docs/handbook/2/generics.html). |
| 163 | + |
| 164 | +## Decorators |
| 165 | + |
| 166 | +With the introduction of Classes in TypeScript and ES6, there now exist certain scenarios that require additional features to support annotating or modifying classes and class members. Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members. Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript. |
| 167 | + |
| 168 | +A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form `@expression`, where `expression` must be a function that will be called at runtime with information about the decorated declaration. For example, given the decorator `@sealed` we might write the `sealed` function as follows: |
| 169 | + |
| 170 | +```ts |
| 171 | +function sealed(target) { |
| 172 | + // do something with 'target' ... |
| 173 | +} |
| 174 | +``` |
| 175 | + |
| 176 | +You can read more about Decorators in detail [on the TypeScript handbook](https://www.typescriptlang.org/docs/handbook/decorators.html). You will most likely not use this in everyday projects as it is a very experimental feature, so we have only paraphrased the basics from the handbook and made you aware of the existence of this advanced type. |
| 177 | + |
| 178 | +## Conclusion |
| 179 | + |
| 180 | +Now we have taken a look at basic everyday types as well as complex advanced types. Were you able to visualize how these types being applied could help in reducing bugs in our code? Slowly you will start to get the hang of TypeScript with more practice. Let us now explore how the TypeScript compiler works in the next lesson. |
| 181 | + |
| 182 | +--- |
| 183 | + |
| 184 | +## References |
| 185 | + |
| 186 | +- https://www.youtube.com/watch?v=BCg4U1FzODs&ab_channel=TraversyMedia |
| 187 | +- https://www.typescriptlang.org/docs/handbook/2/classes.html |
| 188 | +- https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#interfaces |
| 189 | +- https://www.typescriptlang.org/docs/handbook/2/generics.html |
| 190 | +- https://www.typescriptlang.org/docs/handbook/decorators.html |
| 191 | +- https://gist.github.com/bradtraversy/f80a4cd87e7034bea5264f7d8c431b4e |
0 commit comments