Реализация различных классов TypeScript с одинаковым интерфейсом. Проверка полей их экземпляров - PullRequest
1 голос
/ 14 марта 2020

попробуйте реализовать 2 класса для некоторых бизнес-логи c внутри моего приложения, вот некоторый псевдокод (я использую типы object и string, но основная идея - указать каждый тип для каждой логи c )

Имеют некоторые объявления интерфейса и класса;

interface IResult<T, E> {
    result: T;
    err: E;
}

class Fail<E> implements IResult<null, E> {
    private readonly _error: E;

    constructor(error: E) {
        this._error = error;
    }

    get err(): E {
        return this._error;
    }

    get result(): null {
        return null;
    }
}

class Success<T> implements IResult<T, null> {
    private readonly _result: T;

    constructor(result: T) {
        this._result = result;
    }

    get err() {
        return null;
    }

    get result(): T {
        return this._result;
    }
}

Мне нужно получить один из этих экземпляров от некоторого сервиса, например DetailsFactory Я указываю его тип ответа, Success должен return object и Fail должны вернуть string

type Result<T, E> = Success<T> | Fail<E>;

А также использовать для него интерфейс

interface IDetailsFactory {
    make(): Result<object, string>;
}
class DetailsFactory implements IDetailsFactory {
    private readonly _type: string;
    private _operation: object;

    constructor(type: string) {
        this._type = type;
    }

    public make() {
        switch (this._type) {
            case '1': this._operation = {type: '1', name: 'First'}; break;
            case '2': this._operation = {type: '2', name: 'Second'}; break;
            case '3': this._operation = {type: '3', name: 'Third'}; break;
            default: return new Fail('Type is not specified');
        }

        return new Success(this._operation);
    }
}

И я могу использовать его так:

const detailsFactory = new DetailsFactory('1');
const {result, err} = detailsFactory.make();

Здесь я получил ожидаемый объект в поле result и null в err, но когда я пытаюсь проверить

if (!err) {
    console.log(result.name);
}

, я получаю ошибку TS Error:(96, 14) TS2531: Object is possibly 'null'. Конечно, я могу проверить result вместо !err, но это не так красиво, и я хочу сделать ранний выход из функции.

Итак, вопрос: как я могу сказать TS, что если бы я не t любые ошибки (!err = true) доверяют мне и получают данные из поля result? Сделайте так, чтобы он перестал думать, что поле result может быть null, когда error = null

Ответы [ 3 ]

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

Может быть, здесь вам может помочь оператор ненулевого утверждения:

if (!err) {
    console.log(result!.name);
}

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non -null-assertion-operator

1 голос
/ 14 марта 2020

Вы можете использовать различающееся объединение, чтобы заставить компилятор сузить типы, как вы ожидаете. К сожалению, в настоящее время TS не может сузить одну переменную на основе другой, вы можете сузить только одну переменную.

Также вам понадобятся дополнительные универсальные переменные для пересылки фактического возвращаемого типа.

type DetailsFactoryResult = 
     | { type: '1', name: string }
     | { type: '2', name: string }
     | { type: '3', name: string }

class DetailsFactory implements IDetailsFactory<DetailsFactoryResult> {
    // ...    
    public make(): Result<DetailsFactoryResult, string> {
        // ...
    }
}

const detailsFactory = new DetailsFactory("1");
const r = detailsFactory.make();

if (r.err == null) {
    console.log(r.result.name);
}

Playground Link

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

Кроме того, я могу добавить функцию проверки результата в интерфейс и реализовать ее с проверкой типа, например

interface IResult<T, E> {
    result: T;
    err: E;
    isOk(): boolean;
}
class Success<T> implements IResult<T, null> {
    private readonly _result: T;

    constructor(result: T) {
        this._result = result;
    }

    get err() {
        return null;
    }

    get result(): T {
        return this._result!;
    }

    isOk(): this is Success<NonNullable<T>> {
        return Boolean(this._result);
    }
}

И затем я могу использовать ее как

const detailsFactory = new DetailsFactory('1');
const data = detailsFactory.make();

if (data.isOk()) {
    console.log(data.result.name);
}

Но я все еще ищу более элегантное решение и получаю данные путем деструктуризации и простой !err проверки, как в моем вопросе выше

...