Typescript `this is` doğrulaması beklendiği gibi çalışmıyor


Typescript'te Result deseninin açık ve basit bir uygulamasına sahibim:

const OK = 0 as const;
const FAIL = 1 as const;

export abstract class Result<V, E> {
    public readonly abstract _flag: typeof OK | typeof FAIL;
    protected readonly abstract value: V | E;

    public isOk(): this is Ok<V> {
        return this._flag === OK;
    }
    public isFail(): this is Fail<E> {
        return this._flag === FAIL;
    }
}

export class Ok<V> extends Result<V, any> {
    public readonly _flag = OK;

    public constructor(
        public readonly value: V
    ) {
        super();
    }
}

export class Fail<E> extends Result<any, E> {
    public readonly _flag = FAIL;

    public constructor(
        public readonly value: E
    ) {
        super();
    }
}

Ama isOk() ve isFail() yöntemlerinin neden beklendiği gibi doğrulama yapmadığını gerçekten anlayamıyorum. Bakın neler oluyor:

function fn(foo: boolean): Result<string, number> {
    if (foo) return new Ok("OK");
    return new Fail(1);
}

// Here it is correct, the variable is: Result<string, number>
const res = fn(true);

if (res.isOk()) {
    // Here it is not inferring correctly
    // Current inferring: string | number
    // Expected: string
    const val = res.value;
}

Bu arada, eğer tam olarak buradaki gibi bir fonksiyon doğrulaması kullanırsam:

function isOk<V, E>(result: Result<V, E>): result is Ok<V> {
    return result._flag === OK;
}

Ve onu kodda kullanmaya çalıştığımda, beklendiği gibi çalışıyor:

function fn(foo: boolean): Result<string, number> {
    if (foo) return new Ok("OK");
    return new Fail(1);
}

// Result<string, number>
const res = fn(true);

if (isOk(res)) {
    // string
    const val = res.value;
}

Test için tam kod:

const OK = 0 as const;
const FAIL = 1 as const;

export abstract class Result<V, E> {
    public readonly abstract _flag: typeof OK | typeof FAIL;
    public readonly abstract value: V | E;

    public isOk(): this is Ok<V> {
        return this._flag === OK;
    }
    public isFail(): this is Fail<E> {
        return this._flag === FAIL;
    }
}

export class Ok<V> extends Result<V, any> {
    public readonly _flag = OK;

    public constructor(
        public readonly value: V
    ) {
        super();
    }
}

export class Fail<E> extends Result<any, E> {
    public readonly _flag = FAIL;

    public constructor(
        public readonly value: E
    ) {
        super();
    }
}

function fn(foo: boolean): Result<string, number> {
    if (foo) return new Ok("OK");
    return new Fail(1);
}


// First example

// Here it is correct, the variable is: Result<string, number>
const res = fn(true);

if (res.isOk()) {
    // Here it is not inferring correctly
    // Current inferring: string | number
    // Expected: string
    const val = res.value;
}



// Second example

// function isOk<V, E>(result: Result<V, E>): result is Ok<V> {
//     return result._flag === OK;
// }
//
// // Result<string, number>
// const res = fn(true);
//
// if (isOk(res)) {
//     // string
//     const val = res.value;
// }