# Class

## Structure

```typescript
/*
    `extends` tells User is a sub-class of Account. (Only extends ONE class)
    `implements` ensures that User comforms to these `interfaces` or `types`.
*/
class User extends Account implements Updatable, Serializable {
    // The default property being public access, not optional and writable.
    id: string;
    
    // Defines a optional property, which can hold `undefined` or not exist at all.
    middleName?: string;

    /*
        Defines a `trust me, it's there` property.
        (Enforcing to the compiler that this cannot be optional)
    */    
    name!: string;

    /*
        Defines private properties, only accessible to current class.
    */
    private attr: unknown;  // Private only at compilation
    #attr: unknown;         // Private at compilation and runtime
    
    // Defines a property with a default value.
    role = "Student";
    
    /*
       Defines a readonly property.
       Can only be set here at declaration or at the constructor.
    */
    readonly createdAt: Date;

    // See more at `constructor sections` bellow.
    constructor() { ... }
    
    /*
        Describe class methods.
        (Prefer using `arrow function` style to not impact the use of `this`)
        More info bellow.
    */
    setName(name: string) { this.name = name; }
    checkName = (name: string) => { return this.name; }
    
    // Overload method definitions
    sync(): void;
    sync(param: string): string;
    
    // Getter and Setter of properties
    get role() { return this.role.setUpperCase(); }
    set role(value: "Student" | "Teacher") { this.role = value.setLowerCase(); }
    
    /*
        Make `private` or `protected` methods.
        (`protected` are accessible to sub-classes)
    */
    private makeRequest() { ... }
    protected makeRequest() { ... }
    
    // Define `static` properties or methods
    static #userCount = 0;  // static and private
    static registerUser(user: User) { ... }
    
    /*
        Static blocks for setting up static variables.
        (`this` refers to the static class)
    */
    static {
        this.#userCount = -1;
    }
}
```

### `constructor` section

{% hint style="info" %}
`constructor` cannot be overloaded.
{% endhint %}

You could instead of declaring properties at the top, declare them at `constructor's` parameters section.

```typescript
class User {
    // This would be the same as above
    constructor(
        id: string,
        middleName?: string,
        name!: string,
        private attr: unknown,
        role = "Student",
        readonly createdAt: Date
    ) {}
}
```

Or inside `constructor` section. But like this, you cannot assign visibility and other modifiers.

These properties would be declared as default ones. (public, not optional and writable)

And you must provide a value.

```typescript
class User {
    constructor() {
        this.role = "Student";
    }
}
```

### `this` in classes

The value of `this` inside a function depends on how the function is called or defined.

It is not garanteed to always be the class instance.

This problem is refered to as (**losing** `this` **context**).

Read more on [Broken link](https://kdongs.gitbook.io/kdocs/typescript/broken-reference "mention").

**Defining methods with** `method() {}`**:**

Declaring methods like this may implicate that the `this` context is lost and refers to another execution context, leading to runtime errors.

This happens on specific occasions like:

* Passing the class method as Callback or storing it in a variable.
* Using `this` in Event Listeners or Callbacks.

```typescript
// Considering checkName was defined like `checkName() {}` inside the User class
const checkFn = user.checkName;
checkFn();  // Would return undefined
```

```typescript
// Considering checkName was defined like `checkName() {}` inside the User class
class User {
    ...
    delayedDisplay() {
        setTimeout(this.checkName, 1000);  // `this` is lost here
    }
}
```

**Defining methods with** `arrow functions`**:**

Using `arrow functions` when declaring methods in classes is a good way to enforce that `this` will refer to the method's Class.

Since they only inherit the `this` from their enclosing lexical scope.

{% hint style="danger" %}
Declaring methods with arrow functions have a trade off.

Each instance of Person now has its own copy of the method, which increases memory usage, and could have an impact if the class will have a large number of instances.
{% endhint %}

**Binding** `this` **with** `.bind()`**:**

An alternative approach is to explicitly bind `this` using `.bind()`, allowing you to control `this` reference without having to use `arrow functions`.

```typescript
class User {
    ...
    constructor() {
        this.checkName = this.checkName.bind(this);
    }
    
    checkName(name: string) { return this.name; }
    ...
}

const checkFn = user.checkName;
checkFn();  // Correctly return the name
```

## Abstract classes

When declaring classes as not implementable, but as existing to be subclassed in the type system.

You dont instanciate an absctract class.

Can have `absctract` methods.

```typescript
abstract class Item {
    price: number;
    abstract calculateTax(): number;
}

class RealItem extends Item {
    // This pulls from the abstract class
    calculateTax(): number {
        return this.price * 0.1;
    }
}
```

### *Difference of Abstract class and Interface*

Abstract classes can have `partial` implementations. You can code inside them.

*Interfaces cannot have code in them, they only describe typings and method names (Template Methods).*

```typescript
abstract class Item {
    price: number;
    abstract getTax(): number;
    
    calculateTax() {
	return this.price * this.getTax();
    }
}

class RealItem extends Item {
    getTax(): number {
        return 0.1;
    }
}
```

## Decorators and Attributes

You can use decorators on classes, class methods, accessors, properties and parameters to methods.

```typescript
import {
    Syncable, triggerSync, preferCache, required
} from "mylib"

@Syncable
class User {
    @triggerSync()
    save() { ... }
    
    @preferCache(false)
    get displayName() { ... }
    
    udpate(@required info: Partial<User>) { ... }
}
```
