---
# System prepended metadata

title: Study Group

---

# Study Group

## Agenda
Item 49: Provide a Type for this in Callbacks
Item 50: Prefer Conditional Types to Overloaded Declarations
Item 51: Mirror Types to Sever Dependencies


### Item 49: Provide a Type for *this* in Callbacks
#### What is *this* keyword
Dynamically scoped: its value depends not on the way in which it was *defined* but on the way in which it was *called*
Most often used in class => refers to current instance of the class object
```javascript=1
class ExampleClass {
    vals = [];
    constructor(vals) {
        this.vals = vals;
    }
    logSquares() {
        for (const val of this.vals) { console.log(val * val) } 
    }
}
const a = new ExampleClass([1,2,3]);
a.logSquares(); // 1 4 9

const b = new ExampleClass([2,3,4]);
b.logSquares(); // 4, 9, 16
```

When used within an object => refers to the current object
```javascript=1
const exampleObject = {
    vals: [1,2,3],
    logSquares: function() {
        for(const val of this.vals) { console.log(val * val) }
    }
}
exampleObject.logSquares(); // 1 4 9
```

When used within a regular function => refers to the window object 

```javascript=1
function exampleFunc() {
    console.log(this); // window object
}
```

JS provides *bind, call, apply* funcs, so that the programmer can determine the value of *this*

#### Using ExampleClass Example from Above
Now, look what happens when we assign the `logSqaure` function to a variable:
```javascript=1
const a = new ExampleClass([1,2,3]);
const func = a.logSquares;
func(); // Uncaught TypeError: Cannot read property 'vals' of undefined
```

:::spoiler What's going on here?
`a.logSquares` actually does the following
1. calls `ExampleClass.prototype.logSquares`
2. binds the value of *this* in that function to the variable *a*
:::

:::spoiler Solution
1. use bind in constructor to bind *this* to the class instance
2. rewrite `logSquares` as an arrow func
3. use call(apply) to explicitly set *this* 
:::

#### Another common example
```javascript=1
document.querySelector('input')
    .addEventListener('change', function(e) { 
    console.log(this);
});
```
:::spoiler What is *this* going to be?
Input element on which the event fired.
:::

#### What Does *this* Keyword Have to Do with TypeScript?
Because *this* binding is part of JavaScript, TypeScript models it. This means that if you’re writing (or typing) a library that sets the value of *this* on callbacks, then you should model *this*, too.

```javascript=1
function addKeyListener(
el: HTMLElement, 
fn: (this: HTMLElement, e: KeyboardEvent) => void){
    el.addEventListener('keydown', e => {
        fn.call(el, e);
    });
}


declare let el: HTMLElement; 
addKeyListener(el, function(e) { 
    this.innerHTML; // OK, "this" has type of HTMLElement 
});
```
Note: *this* parameter is special: it’s not just another positional argument.

```javascript=1
class Foo {
    registerHandler(el: HTMLElement) {
        addKeyListener(el, e => { this.innerHTML; }); 
    }
}
```
:::spoiler Wrong Implementation
:::danger
If you use an arrow function here, you’ll override the value of *this*. TypeScript will catch the issue
:::


#### Takeaways
1. Understand how *this* binding works
2. Provide a type for *this* in callbacks when it's part of the API


### Item 50: Prefer Conditional Types to Overloaded Declarations
#### How Would You Write Type Declaration for the Following
```javascript=1
function double(x) {
    return x + x;
}
```
The function, `double`, could be passed as a string or a number, so we could use union type and TypeScript's function overloading to achieve type declaration
```javascript=1
function double(x: string|number): string|number;
function double(x: any) { return x + x; }

const num = double(12) // string | number
const str = double('x') // string | number
```
This declaration misses the subtle difference and will produce types that are hard to work with
Might also write using generic
```javascript=1
function double<T extends string | number>(x: T): T;
function double(x: any) { return x + x; };

const num = double(12) // 12
const str = double('x') // 'x'
```
Too precise!
When pass a `string literal`, the type is the same `string literal` type. By passing a `string literal` type `'x'`, the result type should not be `'x'` because 'xx' should be a type string
Another option is to provide multiple type declarations (Note: TypeScripts allows you to write any number of type declarations, but only **ONE** implementation of the function)
```javascript=1
function double(x: number): number;
function double(x: string): string;
function double(x: any) { return x + x; };

const num = double(12); // number
const str = double('x'); // string
```
:::spoiler FINALLY
```javascript=1
function f(x : number | string) {
    double(x); // ERROR!!!
}
```
NO, it will introduce a bug when double is being used in another function call that passes a `string` or a `number` as param
When overload type declaration, TypeScript checks from top to bottom
TypeScript will complain at the last overload because `string | number` is not assignable to `string`
You may wonder you could have added another type overload for the case of `string | number`, but that is not ideal
The best solution for this case is to use a `conditional type`
:::

#### Conditional Types
like if statements in type space

```javascript=1
function double<T extends number | string>(x : T): T extends string ? string : number;
function double(x: any) { return x + x; }
```
Similar with using a generic, but with a more elaborate return type
Read the return type as you would when using a ternary operator (?:)
1. If `T` is a subset of `string` (`string` or `string literal(s)`), then return type is `string`
2. Otherwise, return type is `number`

#### Takeaways
1. Conditional types is a technique that can achieve type distributions with overloaded type declarations 


### Item 51: Mirror Types to Sever Dependencies
Given the following scenario:
- Written a library for parsing CSV files
- Function is to pass in contents of CSV and get back a parsed object
- For the convenience for NodeJS users, you allow the contents to be either a `string` or a `NodeJS Buffer`
```javascript=1
function parseCSV(contents: string | Buffer): {[column: string]: string}[] {
    if (typeof contents === 'object') {
        // It's a buffer
        return parseCSV(contents.toString('utf8'));
    }
    // ...
}
```
The type definition for Buffer comes from the NodeJS type declarations, so must install

`npm install --save-dev @types/node`
Since type declarations depend on the NodeJS types, you include `@types/node` as a `devDependency`

:::spoiler Users will start complaining
- JavaScript developers (what is the @type module doing?)
- TypeScript developers (why depending on NodeJS?)

Their concerns are reasonable because 
1. `Buffer` behavior isn't essential
2. Only relevant for users who are using NodeJS
3. `@types/node` is only relavant to NodeJS users who are also using TypeScipt
:::

#### Solution
TypeScript's structural typing is a solution for the scenario
Rather tahn using the declaration of `Buffer` from `@types/node`, can write your own with just the methods and properties you need
In this case, we just need a toString method that takes an encoding parameter

```javascript=1
interface CsvBuffer {
    toString(encoding: string) : string;
}

function parseCSV(contents: string | CsvBuffer): {[column: string]: string}[] {
    // ...
}
```

The interface is shorter than the complete one, but it does what the code needs from a `Buffer`
In NodeJS project will work as well because types are compatible
```javascript=1
parseCSV(new Buffer("column1,column2\nval1,val2", "utf-8")) // OK
```
If your library only depends on the types for another library, might consider just mirroring the declarations you need
If you depend on the implementation of a lib, still may be able to apply the same trick to avoid depending on its typings
However, becomes difficult when dependence grows larger and more essential
- In this case, make sure you formalize the relationship by making the @types dependency explicit
This technique is also helpful for severing dependencies between UT and prod
```javascript=1
// Query results from a PostgresDB

interface Author {
    first: string;
    last: string;
}

function getAuthors(database: PostgresDB): Author[] {
    const authorRows = database.runQuery(`SELECT FIRST, LAST FROM AUTHORS`);
    return authorRos.map(row => ({ first: row[0], last: row[1] }));
}

// To test this, have to mock PostgresDB.
// Better approach with structural typing 

interface DB {
    runQuery: (sql: string) => any[];
}

function getAuthors(database: DB): Author[]{
    const authorRows = database.runQuery(`SELECT FIRST, LAST FROM AUTHORS`);
    return authorRows.map(row => ({ first: row[0], last: row[1] }));
}

// Test
test('getAuthors', () => {
    const authors = getAuthors({
        runQuery(sql: string) {
            return [['Toni', 'Morrison'], ['Maya', 'Angelou']];
        }
    });
    expect(authors).toEqual([
        { first: 'Toni', last: 'Morrison' },
        { first: 'Maya', last: 'Angelou' }
    ]);
});
```

#### Takeaways
1. Use structrual typing to sever dependencies that are trivial
2. Don't force JS users to depend on @types
3. Don't force web developers to depend on NodeJS

