Search code examples
typescriptnestjstype-safety

TypeScript accepts non-number/string value although the expected parameter type is a number


I am facing an odd issue with TypeScript which I fail to understand. It could be related to my lack of knowledge as I never faced such issue in Java and it is my first (bug-fixing) steps in TypeScript / NestJS.

It seems the type safety is not guaranteed in TypeScript and I would like to know if it works as "designed" or if there is a reason for it. Here a minimal example to reproduce the issue:

  async findAll(@Query() query, @Res() res: Response) {
      ... lines omitted ...
      this.cache.getProxyCache(query.page, query.page_size);
      ... lines omitted ...
  }

The query is an object that I got from @Query decorator in NestJS controller.

  async getProxyCache(page: number = 0, pageSize: number): Promise<AxwayProxy[]> {
    console.log(`page: ${page} typeof: ${typeof page}`);
    console.log(`pageSize: ${pageSize} typeof: ${typeof pageSize}`);

    let pageSizeAdded = pageSize + 3;
    console.log(`pageSizeAdded: ${pageSizeAdded} typeof: ${typeof pageSizeAdded}`);

    let pageSizeAdded2 = Number(pageSize) + 3;
    console.log(`pageSizeAdded2: ${pageSizeAdded2} typeof: ${typeof pageSizeAdded2}`);
    ... lines omitted ...

The output is (watch out for pageSizeAdded which is the bug, pageSizeAdded2 is calculated correctly, because I converted from string to number):

page: 1 typeof: string
pageSize: 4 typeof: string
pageSizeAdded: 43 typeof: string
pageSizeAdded2: 7 typeof: number

I am wondering that page and pageSize are type of string although the function parameter is declared with number type.

It seems TypeScript shows an error message when I try to call the function directly with string values ( e.g. this.cache.getProxyCache('1', '2'); ) but accept a non-number value when it is passed from another object.

Did anyone faced same issue? It is a bug, known limitation? Why it is allowed?

Thanks, Christoph


Solution

  • You have to understand that TypeScript will be converted to JavaScript before execution. So all the typing will disappear. When you declare a function like

    async getProxyCache(page: number = 0, pageSize: number): Promise<AxwayProxy[]>
    

    you ask for TypeScript to check that the parameters and return type are of appropriate type. But this check is done only at transpilation (and dev, with a good IDE) time. At runtime, the types will disappear, and you can provide whatever you want, there will neither be a control nor a cast. Only the JavaScript part will remain:

    async getProxyCache(page = 0, pageSize)
    

    If you wonder why TypeScript accepted that you call your function with anything that is not a number, then check the type of page where the function is called. If it is of type any, then it is declared as compatible with anything. In such case, it would probably better to declare it as unknown which would require to check that the type is appropriate before using it.