Не удается заставить перегрузку метода работать вместе с дискриминационным объединением.Что случилось? - PullRequest
2 голосов
/ 02 июля 2019

Как заставить следующий код работать без утверждения as IUpdateRequestPromiseProxy<TEntity>?

import { Id } from 'app/core/persistence';
import { IPersistence } from '..';
import { IBaseEntity } from '../../..';

/*
 * Missing changeFn indicates delete
 */
type IChangeRequestPromiseProxy<TEntity extends IBaseEntity> =
    | IUpdateRequestPromiseProxy<TEntity>
    | IDeleteRequestPromiseProxy;

interface IUpdateRequestPromiseProxy<TEntity extends IBaseEntity> {
    id: Id;
    kind: 'update';
    resolve(value: IPersistence<TEntity>): void;
    reject(reason: any): void;
    changeFn(entity: IPersistence<TEntity>): IPersistence<TEntity>;
}

interface IDeleteRequestPromiseProxy {
    id: Id;
    kind: 'delete';
    resolve(value: void): void;
    reject(reason: any): void;
}

export class QueueService<TEntity extends IBaseEntity> {
    private changeRequestsQueue: Array<IChangeRequestPromiseProxy<TEntity>> = [];
    private flushingQueueMutex: boolean = false;

    public async enqueue(id: Id, kind: 'delete'): Promise<void>;
    public async enqueue(
        id: Id,
        kind: 'update',
        changeFn: (entity: IPersistence<TEntity>) => IPersistence<TEntity>,
    ): Promise<IPersistence<TEntity>>;
    public async enqueue(
        id: Id,
        kind: 'delete' | 'update',
        changeFn?: (entity: IPersistence<TEntity>) => IPersistence<TEntity>,
    ): Promise<IPersistence<TEntity> | void> {
        let promise: Promise<IPersistence<TEntity> | void>;
        switch (kind) {
            case 'delete':
                promise = new Promise<void>(async (resolve, reject) => {
                    this.changeRequestsQueue.push({
                        id,
                        kind,
                        resolve,
                        reject,
                    });
                });
                break;
            case 'update':
            default:
                promise = new Promise<IPersistence<TEntity>>(async (resolve, reject) => {
                    this.changeRequestsQueue.push({
                        id,
                        kind,
                        resolve,
                        reject,
                        changeFn,
                    } as IUpdateRequestPromiseProxy<TEntity>);
                });
                break;
        }

        return promise;
    }
}

Если я удалю утверждение, я получу ошибку:

Argument of type '{ id: string; kind: "update"; resolve: (value?: IPersistence<TEntity> | PromiseLike<IPersistence<TEntity>> | undefined) => void; reject: (reason?: any) => void; changeFn: ((entity: IPersistence<TEntity>) => IPersistence<...>) | undefined; }' is not assignable to parameter of type 'IChangeRequestPromiseProxy<TEntity>'.
  Type '{ id: string; kind: "update"; resolve: (value?: IPersistence<TEntity> | PromiseLike<IPersistence<TEntity>> | undefined) => void; reject: (reason?: any) => void; changeFn: ((entity: IPersistence<TEntity>) => IPersistence<...>) | undefined; }' is not assignable to type 'IUpdateRequestPromiseProxy<TEntity>'.
    Types of property 'changeFn' are incompatible.
      Type '((entity: IPersistence<TEntity>) => IPersistence<TEntity>) | undefined' is not assignable to type '(entity: IPersistence<TEntity>) => IPersistence<TEntity>'.
        Type 'undefined' is not assignable to type '(entity: IPersistence<TEntity>) => IPersistence<TEntity>'.ts(2345)

1 Ответ

2 голосов
/ 02 июля 2019

Проблема заключается в том, что машинопись не отслеживает отношения между kind и changeFn, создаваемые перегрузками.Таким образом, changeFn все еще может быть неопределенным в отношении ts, даже если вы находитесь на ветви kind === 'update'.

Самое простое решение - использовать ненулевое утверждение:

this.changeRequestsQueue.push({
    id,
    kind: 'update',
    resolve,
    reject,
    changeFn: changeFn!,
});

Если вам нужна полная версия без утверждений, это можно сделать с помощью различенного кортежа в параметрах, хотя я не уверен, что это стоит дополнительной сложности:

export class QueueService<TEntity extends IBaseEntity> {
    private changeRequestsQueue: Array<IChangeRequestPromiseProxy<TEntity>> = [];
    private flushingQueueMutex: boolean = false;

    public async enqueue(id: Id, kind: 'delete'): Promise<void>;
    public async enqueue(
        id: Id,
        kind: 'update',
        changeFn: (entity: IPersistence<TEntity>) => IPersistence<TEntity>,
    ): Promise<IPersistence<TEntity>>;
    public async enqueue(
        id: Id,
        ...r: ['delete'] | ['update', (entity: IPersistence<TEntity>) => IPersistence<TEntity>],
    ): Promise<IPersistence<TEntity> | void> {
        let promise: Promise<IPersistence<TEntity> | void>;
        switch (r[0]) {
            case 'delete':
                promise = new Promise<void>(async (resolve, reject) => {
                    this.changeRequestsQueue.push({
                        id,
                        kind: r[0],
                        resolve,
                        reject,
                    });
                });
                break;
            case 'update':
            default:
                promise = new Promise<IPersistence<TEntity>>(async (resolve, reject) => {
                    this.changeRequestsQueue.push({
                        id,
                        kind: r[0],
                        resolve,
                        reject,
                        changeFn: r[1],
                    });
                });
                break;
        }

        return promise;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...