typescripttypestypescript-genericsangular10

Typescript generic type issue


I have this code which was working fine in "typescript": "3.8.3" after upgrading to Angular 10 and "typescript": "4.0.8" I see some typescript errors:

handleEventResponse<T extends any>(event: CustomEvent, { resolve, reject }): T {
    let response: T = {} as T;
    if (Array.isArray(event.data)) {
      response = event.data as any;
      resolve(response);
    } else {
      Object.assign(response, event.data);
      if (response.status === 'ok') {
        resolve(response);
      } else {
        reject(response.error);
      }
    }

    return response;
}

Having these compile errors :

Compiling TypeScript sources through ngc
ERROR: src/lib/services/custom-events.service.ts:3

362       if (response.status === 'ok') {
                       ~~~~~~
src/lib/services/custom-events.service.ts:365:25 -

365         reject(response.error);
                            ~~~~~

Any suggestion to fix it will be appreciated.


Solution

  • Problem

    Your method has a generic type constrained to any:

    handleEventResponse<T extends any>(...) { ... }
    

    In TypeScript 3.9 the following change was introduced:

    Type Parameters That Extend any No Longer Act as any

    In previous versions of TypeScript, a type parameter constrained to any could be treated as any.

    function foo<T extends any>(arg: T) {
      arg.spfjgerijghoied; // no error!
    }
    

    This was an oversight, so TypeScript 3.9 takes a more conservative approach and issues an error on these questionable operations.

    function foo<T extends any>(arg: T) {
     arg.spfjgerijghoied;
      //  ~~~~~~~~~~~~~~~
      // Property 'spfjgerijghoied' does not exist on type 'T'.
    }
    

    This results in T being treated as unknown. You cannot do anything with a value typed as unknown because it is not type safe. In your code, there's no guarantee that status or error will be available.

    Solution

    You'll need to tell TypeScript that status and error may be available on type T. An example:

    interface Response {
      status: <your type>;
      error: <your type>;
    }
    
    handleEventResponse<T extends Response>(...) { ... }