TypeScript generics: what if Promise reject and resolve types differ?
Asked Answered
T

1

6

Simple and common Angular2+ data service that handles errors and uses Promises:

// imports skipped

class DataType {}

class Error {
  constructor(error: HttpResponse) {
    this.error = error.json()
  }
}


@Injectable()
export class MyService<DataType> {
  apiUrl = 'my/url';
  constructor(protected http) {}

  get(): Promise<DataType[] | Error> {
    return this.http
    .get(this.apiUrl)
    .toPromise()
    .then(response => response.json())
    .catch(this.handleError);
  }

  protected handleError(error: HttpResponse): Promise<Error> {
    console.error('An error occurred', error);
    return Promise.reject(new Error(error));  
  }
}

In this class, get method returns a Promise, which resolves with DataType[] array, and rejects with Error type.

Compiling this code, I'm getting this error:

Type 'Error | DataType[]' is not assignable to type 'DataType'. Type 'Error' is not assignable to type 'DataType'.

I'm using TypeScript version 2.4.2. I'm sure this started happening after TypeScript upgrade, which made types checks more strict.

I want to know how to handle rejection with different type in newest TypeScript. Are there different practices for doing this?

Update: I've found a corresponding bug report, not solved yet https://github.com/Microsoft/TypeScript/issues/7588

UPDATE

I've created a repo demonstrating the problem, with two commits. First commit uses <TypeResolve | TypeReject> approach, second uses <TypeResolved> only. Both commits seem to not compile in real life.

Tungus answered 13/8, 2017 at 7:30 Comment(6)
The generic type constraint for promises only refers to the resolve type. The reject type is always any.Yodle
@NitzanTomer it seems to me you are not right. Typescript expects reject type to be the same as resolve typeTungus
No, it does not. The reject type is always any, regardless of the resolve type. When you say Promise<string> then only the resolve type is string, the reject type can be anything (any)Yodle
You don't want Promise<DataType[] | Error> you just want Promise<DataType[]>Calculator
@NitzanTomer @Calculator I've created a repo where I have put code that does not compile in any way, with ` | Error` or without it. github.com/easy-one/promise_typescriptTungus
That's way too much code to go through. Please reproduce the problem in playground, that would be the best.Yodle
B
7

The type parameter for Promise is only the type for .resolve, not for .reject. The type for .reject is always any, because the type cannot be known at compile time. One of the reasons for this is that async functions, which return promises, could potentially reject their promises with any exception.

You should write Promise<DataType[]> instead, and check the error type at runtime where you handle errors.

Brindisi answered 13/8, 2017 at 16:39 Comment(1)
I may mistake, but Typescript throws an exception in my case. I hope this is a bug they will fixTungus

© 2022 - 2024 — McMap. All rights reserved.