Машинопись: правильная типизация с помощью дженериков - PullRequest
0 голосов
/ 14 марта 2020

Я недавно переключился с JavaScript на TypeScript, что действительно здорово. Во время моей ежедневной работы мне было интересно, как Promises работает под капотом, и после короткого поиска в Google я нашел эту прекрасную статью: https://softchris.github.io/pages/javascript-promises.html#why. Тогда я подумал, что было бы неплохо преобразовать это в TypeScript. Ну, я получил его на работу, как вы можете видеть ниже. Одна вещь, которую я не мог исправить, это то, что data, onSuccess и onFailure все еще имеют any вместо generi c в then<R>(){}. Тот факт, что error является any, был преднамеренным.

Так может кто-нибудь помочь мне исправить это? Для игры с кодом вы можете использовать https://stackblitz.com/edit/swear-ts.

Большое спасибо! :)

type Resolver<T> = (x: T) => void;
type Rejecter = (x: any) => void;

type Factory<T> = (resolve: Resolver<T>, reject?: Rejecter) => void;

type OnSuccessFn<T, R> = (data: T) => R;
type OnFailureFn<R> = (error: any) => R;

class Swear<T = any> {
  private data: any;

  private resolveWasInvoked = false;
  private errorWasHandled = false;

  private onSuccess: OnSuccessFn<T, any>;
  private onFailure?: OnFailureFn<any>;

  constructor(
    private factory: Factory<T>,
    private error: null | Error = null
  ) { }

  private resolve(data: T): void {
    if (!this.error) {
      const result = this.onSuccess(data);

      if (result) {
        this.data = result;
      }

      this.resolveWasInvoked = true;
    }
  }

  private reject(error: any): void {
    if (!this.resolveWasInvoked) {
      this.error = error;

      if (this.onFailure) {
        const result = this.onFailure(error);

        if (result) {
          this.data = result;
        }

        this.errorWasHandled = true;
      }
    }
  }

  public then<R>(
    OnSuccessFn: OnSuccessFn<T, R>,
    OnFailureFn?: OnFailureFn<R>
  ): Swear<R> {
    this.onSuccess = OnSuccessFn;
    this.onFailure = OnFailureFn;

    this.factory(
      (data: T): void => this.resolve(data),
      (error: any): void => this.reject(error),
    )

    return new Swear<R>((resolve) => {
      resolve(<R>this.data);
    }, !this.errorWasHandled ? this.error : null);
  }

  catch(catchFn: (error: any) => void): void {
    if (!this.errorWasHandled && this.error) {
      catchFn(this.error);
    }
  }
}

const swear: Swear<string> = new Swear((resolve, reject) => {
  resolve('Hello');
  // reject('Ups..');
})

swear
  .then(data => data + ' World!')
  // .then(data => data.length)
  .then(data => console.info(data))
  .catch(error => console.error(`Catched: ${error}`));

1 Ответ

0 голосов
/ 14 марта 2020

Реализовал какой-то базовый c рефакторинг, чтобы избавиться от любого типа. Поскольку ваша функция onSuccess имеет два обобщенных типа c, один предназначен для ввода, а второй возвращается. Я добавил тот же шаблон c в ваш класс, чтобы удалить большую часть любого использования. Кроме того, ошибка имеет тип Ошибка в машинописи, вам не нужно использовать любой тип.

    type Resolver<T> = (x: T) => void;
    type Rejecter = (x) => void;

    type Factory<T> = (resolve: Resolver<T>, reject?: Rejecter) => void;

    type OnSuccessFn<T, R> = (data: T) => R;
    type OnFailureFn<R> = (error: Error) => R;

    class Swear<T, R> {
     private data: R;
     private resolveWasInvoked = false;
     private errorWasHandled = false;

     private onSuccess: OnSuccessFn<T, R>;
     private onFailure?: OnFailureFn<R>;

     constructor(
       private factory: Factory<T>,
       private error: null | Error = null
     ) { }

     private resolve(data: T): void {
     if (!this.error) {
         const result = this.onSuccess(data);

        if (result) {
          this.data = result;
        }
        this.resolveWasInvoked = true;
      }
    }

   private reject(error: Error): void {
   if (!this.resolveWasInvoked) {
       this.error = error;

       if (this.onFailure) {
          const result = this.onFailure(error);

       if (result) {
          this.data = result;
      }
      this.errorWasHandled = true;
    }
  }
}

 public then(
    OnSuccessFn: OnSuccessFn<T, R>,
    OnFailureFn?: OnFailureFn<R>
   ) {
       this.onSuccess = OnSuccessFn;
       this.onFailure = OnFailureFn;

       this.factory(
       (data: T): void => this.resolve(data),
       (error: Error): void => this.reject(error),
      )

       return new Swear<any, R>((resolve) => {
       resolve(<R>this.data);
     }, !this.errorWasHandled ? this.error : null);
  }

  catch(catchFn: (error: Error) => void): void {
    if (!this.errorWasHandled && this.error) {
      catchFn(this.error);
    }
  }
}
const swear: Swear<string, void> = new Swear((resolve, reject) => {
    resolve('Hello');
    // reject('Ups..');
 })

swear
  .then(data => data + ' World!')
  .then(data => console.log(data))
  .catch(error => console.error(`Catched: ${error}`));code here
...