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;
// }