[TypeScript] Type check
Tags: typescript
Published: 2021-08-08 02:35:55

## Intro
When I code with TypeScript, I often check the type of variables or arguments of functions.
Most of them are null checking. But others are more complicated.
Because I want to know how the type checking becomes more simple, I will try type checking of TypeScript.

## typeof
If the target type belongs object types like "string", "number", etc., I can use "typeof" operator for type checking.
Because TypeScript has type guards, I can treat the argument as string value.

```ts
function check(target: any): boolean {
    if(typeof target === "string") {
        console.log(target.toUpperCase());        
        return true;
    }
    return false;
}
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/di2gam16scknkuml4sg6.jpg)

* [Typeof Type Operator - TypeScript Documentation](https://www.typescriptlang.org/docs/handbook/2/typeof-types.html)

## instanceof
If the target type is class, I can use "instanceof" operator.

```ts
class ClassSample {    
    public id = -1;
    public name = 'Hello';
    public greet(message: string): void {
        console.log(`${message}`);
    }
}
export function init(): void {
    const c = new ClassSample();    
    const checkResult = check(c);
    // CHECK true
    console.log(`CHECK ${checkResult}`);
}

function check(value: any): boolean {
    if(value instanceof ClassSample) {
        // "value" is treated as ClassSample
        value.greet('Hello');
        return true;
    }
    return false;
}
```

This operator checks types by prototype property.

```ts
class ClassSample {    
    public id = -1;
    public name = 'Hello';
    public greet(message: string): void {
        console.log(`${message}`);
    }
}
class ClassSample2 extends ClassSample {
    public greet2(message: string): void {
        console.log(`${message}`);
    }
}
type TypeSample = {
    id: number,
    name: string,
    greet: (message: string) => void,
};
export function init(): void {
    const c = new ClassSample();
    const c2 = new ClassSample2();
    const t: TypeSample = {
        id: 3,
        name: 'world',
        greet: (message: string) => alert(message),
    };
    
    const checkResult = check(c);
    // CHECK true
    console.log(`CHECK ${checkResult}`);

    // extended class also get true
    const checkResult2 = check(c2);
    // CHECK true
    console.log(`CHECK2 ${checkResult2}`);
    
    // "instanceof" isn't check the properties
    const checkResult3 = check(t);
    // CHECK false
    console.log(`CHECK3 ${checkResult3}`);
}
...
```

* [instanceof - JavaScript|MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof)

## Check types or interfaces
I can't use "typeof" and "instanceof" to check "type" or "interface".

Because all of the return values of "typeof" are "object".
And because JavaScript doesn't has "type" and "interface", I can't use "instanceof".

So I have to use custom type guards.

```ts
...
function check(value: any): boolean {
    if(checkTypeSample(value)) {
        return true;
    }
    return false;
}
function checkTypeSample(value: any): value is TypeSample {
    if(typeof value !== 'object') {
        return false;
    }
    // check the property exists by "in" operator
    if('id' in value === false) {
        return false;
    }
    if(typeof value.id !== 'number') {
        return false;
    }
    if('name' in value === false) {
        return false;
    }
    if(typeof value.name !== 'string') {
        return false;
    }
    if('greet' in value === false) {
        return false;
    }
    if(typeof value.greet !== 'function') {
        return false;
    }
    return true;
}
```

If the return value of "checkTypeSample" is true, I can treat the value as "TypeSample".
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxg6ijm1senaz3ywhjx0.jpg)

* [Narrowing - TypeScript Documentation](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)
* [Type Guard - TypeScript Deep Dive](https://basarat.gitbook.io/typescript/type-system/typeguard)

### Check functions?
One problem is I only can check the function in two ways.

1. The function name is 'greet'
2. The type of function is 'function'

So after I add another type like below, I can't distinguish them with "checkTypeSample".

```ts
...
type TypeSample = {
    id: number,
    name: string,
    greet: (message: string) => void,
};
type TypeSample2 = {
    id: number,
    name: string,
    greet: (id: number, message: string) => void,
};
export function init(): void {
    const t: TypeSample = {
        id: 3,
        name: 'world',
        greet: (message: string) => alert(message),
    };
    const t2: TypeSample2 = {
        id: 4,
        name: 'Hello world',
        greet: (id: number, name: string) => console.log(`Hello ${id}:${name}`),
    };
    // T result:true
    console.log(`T result:${checkTypeSample(t)}`);
    // T2 result:true
    console.log(`T2 result:${checkTypeSample(t2)}`);
}
...
```
#### Get arguments
Unfortunately "Function.arguments" can no longer be used.

* [Function.arguments - JavaScript|MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/arguments)

After all, I get a string value of the function and split text to get arguments.

```ts
...
function checkTypeSample(value: any): value is TypeSample {
...
    if('greet' in value === false) {
        return false;
    }
    if(typeof value.greet !== 'function') {
        return false;
    }
    // (message) => alert(message)
    const greetText = value.greet.toString();
    // if the number of arguments is more than 2, I should use "/\([0-9|a-z|A-Z|,|\s]+\)/g"
    const splittedGreetTexts = greetText.match(/\([0-9|a-z|A-Z]+\)/g);
    // I can only check the number of arguments of the function.
    if(splittedGreetTexts == null ||
            splittedGreetTexts.length <= 0 ||
            splittedGreetTexts[0] == null) {
        return false;
    }    
    return true;
}
```