Last Sync: 2022-09-20 14:30:05

This commit is contained in:
tactonbishop 2022-09-20 14:30:05 +01:00
parent 27e23cdd56
commit 7aa8a034c7

View file

@ -120,4 +120,92 @@ class Programmer implements Person {
## Inheritance
We can extend classes in TypeScript by using sub-classes or abstract classes.
### Sub-classes
In the case of sub-classes, we use the phrase `[child_class] extends [parent_class]` in the class declaration to designate the inheritance relationship between the base class and the new class that is derived from it.
> A derived class has all the properies and methods of its base class but can also define additional members.
When you instantiate a child class from a parent class, if the parent class has constructor values, you must initialise these in the child. You do this by calling the parent constructor via the `super()` syntax. For example:
```ts
class Employee extends Person {
constructor(firstName: string, lastName: string, private jobTitle: string) {
// call the constructor of the Person class:
super(firstName, lastName);
}
}
```
To override or alter a method that exists on the parent in the child, you can use the syntax `super.[methodName]()`.
### Abstract classes
Classes marked `abstract` are similar to parent in the case of sub-classes. The difference is that they are like templates. Several of their methods and properties may be directly inherited by classes that derive from them (just like sub-classes) but they can include 'blank' methods and properties that are placeholders for methods and properties that are defined in the derivation class.
I have found this useful for cases where you want to inherit methods from a parent class but implement a specific method differently in each derivation.
```ts
export abstract class IndexHyperlinksProvider
implements vscode.TreeDataProvider<TreeItem>
{
public activeFile: string | undefined
private outlinks
private fileSystemUtils: FileSystemUtils
constructor(
activeFile: string | undefined,
workspaceRoot: string | undefined
) {
this.setActiveFile(activeFile)
this.outlinks = this.generateLinks()
this.fileSystemUtils = new FileSystemUtils(workspaceRoot)
}
abstract generateLinks(): Promise<TreeItem[] | undefined>
public setActiveFile(activeFile: string | undefined) {
this.activeFile = activeFile
}
...
```
Above we have a base class with a single abstract method `generateLinks()`. The class below extends this base. Note that it passes the parent constructor values to `super` and defines an actual method for the designated `generateLinks()` template:
```ts
export class IndexOutlinksProvider extends IndexHyperlinksProvider {
public workspaceFiles: string[];
public context: vscode.ExtensionContext;
constructor(
activeFile: string | undefined,
workspaceRoot: string | undefined,
workspaceFiles: string[],
context: vscode.ExtensionContext
) {
super(activeFile, workspaceRoot);
this.workspaceFiles = workspaceFiles;
this.context = context;
}
public async generateLinks(): Promise<TreeItem[] | undefined> {
const indexer = new IndexHyperlinks(this.context, this.workspaceFiles);
if (typeof this.activeFile === "string") {
const outlinks = await indexer.indexOutlinks(this.activeFile);
if (outlinks !== undefined) {
return this.transformLinksToTreeItem(outlinks);
}
}
return;
}
}
```
> Importantly, you cannot instantiate abstract classes. You can only `extend` them and then instantiate their derivation. This is another important difference from sub-classes.
As with sub-classes, you must initialise the properties of the parent constructor with `super`.
### `implements` vs `extends`
You shouldn't confuse `implements` with `extends`. `implements` just checks the class as an interface in accordance with the principles of [duck typing](/Programming_Languages/TypeScript/Custom_types.md#duck-typing): i.e the implementing class should have the same properties and methods. It doesn't affect anything internal to the methods or properties. So e.g, if you typed a method parameter as `string` in the base class, this would still default to `any` in the derived class.