联合类型通用通知的类型保护(Notifier | Success)不起作用
Type guard for union type generic notification (Notifier | Success) does not work
我有以下结构:
type Result<TNotification, TSuccess> = Success<TNotification, TSuccess> | Notifier<TNotification, TSuccess>;
abstract class ResultAbstract<TNotification, TSuccess> {
protected constructor(protected readonly result: TNotification | TSuccess) {}
abstract isSuccess(): this is Success<TNotification, TSuccess>;
abstract isFailure(): this is Notifier<TNotification, TSuccess>;
abstract getData(): TNotification | TSuccess;
}
class Success<F, S> extends ResultAbstract<F, S> {
constructor(readonly data: S) {
super(data);
}
isSuccess(): this is Success<F, S> {
return true;
}
isFailure(): this is Notifier<F, S> {
return false;
}
getData(): S {
return this.result as S;
}
}
class Notifier<E, _> extends ResultAbstract<E, _> {
constructor(readonly data: E) {
super(data);
}
isSuccess(): this is Success<E, _> {
return false;
}
isFailure(): this is Notifier<E, _> {
return true;
}
getData(): E {
return this.result as E;
}
}
class Fail {}
class User {
constructor(private name: string){}
}
我基本上是在尝试构建一个通知模式来处理我的代码流中的异常。我有以下功能:
const findUser = (): Result<Fail, User> => {
if(Math.random() * 0.5 < 0.2)
return new Success(new User('Tony stark'))
return new Notifier(new Error('Usuário não existe'))
}
const userExists = () => {
const result = findUser()
if(result.isFailure()) return 'Not found'
const user = result.getData() // Property 'getData' does not exist on type 'never'.(2339)
return user.name
}
console.log(userExists())
Result 类型是 Notifier (For errors) 或 Success 的联合体,但如果我在联合体上调用其中一个类型守卫,它不会将余数(“else”语句)缩小到另一半工会的。
如果我显式调用第二个类型保护,它会按预期工作:
const userExists = () => {
const result = findUser()
if(!result.isSuccess()) return 'Not found'
const user = result.getData()
return user.name
}
console.log(userExists()) // it works!
The sample code is here in this playground
谁能帮我理解一下?
这里问题的核心是Success
和Notifier
在结构上都是一样的,所以TypeScript认为如果后面的if语句有机会returning true
对于其中一个 类,它 return 对两者都是正确的:
if(result.isFailure())
因此,此块之后的 result
值类型被自动推断为 never
。
我创建了一个示例来说明这一点 here.
通过在两个 类 之一中引入唯一 field/method 或添加私有 field/method.
可以轻松解决此问题
以上说明将解决您遇到的问题,并可用于描述 if(result.isFailure())
语句的行为。但是,另一方面,if(result.isSuccess())
似乎仍然适用于您的原始代码。这样做的原因是因为您按照嵌套泛型类型分配给扩展 类:
的顺序引入了动态性
extends ResultAbstract<F, S>
对比 extends ResultAbstract<E, _>
鉴于上述情况,类 可以被认为是不同的,但是 TypeScript 仍然认为 Success
和 Notifier
与 is
谓词相同告诉它 Success
可以是 Notifier
(通过 isFailure
方法)并且 Notifier
可以是 Success
(通过 isSuccess
方法)。因此,您需要做的就是删除 isSuccess
或 isFailure
方法,代码将开始工作,因为它会告诉 TypeScript 它只能是一个。
因为你的代码只使用了两个类,这将是最合适的解决方案。
我有以下结构:
type Result<TNotification, TSuccess> = Success<TNotification, TSuccess> | Notifier<TNotification, TSuccess>;
abstract class ResultAbstract<TNotification, TSuccess> {
protected constructor(protected readonly result: TNotification | TSuccess) {}
abstract isSuccess(): this is Success<TNotification, TSuccess>;
abstract isFailure(): this is Notifier<TNotification, TSuccess>;
abstract getData(): TNotification | TSuccess;
}
class Success<F, S> extends ResultAbstract<F, S> {
constructor(readonly data: S) {
super(data);
}
isSuccess(): this is Success<F, S> {
return true;
}
isFailure(): this is Notifier<F, S> {
return false;
}
getData(): S {
return this.result as S;
}
}
class Notifier<E, _> extends ResultAbstract<E, _> {
constructor(readonly data: E) {
super(data);
}
isSuccess(): this is Success<E, _> {
return false;
}
isFailure(): this is Notifier<E, _> {
return true;
}
getData(): E {
return this.result as E;
}
}
class Fail {}
class User {
constructor(private name: string){}
}
我基本上是在尝试构建一个通知模式来处理我的代码流中的异常。我有以下功能:
const findUser = (): Result<Fail, User> => {
if(Math.random() * 0.5 < 0.2)
return new Success(new User('Tony stark'))
return new Notifier(new Error('Usuário não existe'))
}
const userExists = () => {
const result = findUser()
if(result.isFailure()) return 'Not found'
const user = result.getData() // Property 'getData' does not exist on type 'never'.(2339)
return user.name
}
console.log(userExists())
Result 类型是 Notifier (For errors) 或 Success 的联合体,但如果我在联合体上调用其中一个类型守卫,它不会将余数(“else”语句)缩小到另一半工会的。
如果我显式调用第二个类型保护,它会按预期工作:
const userExists = () => {
const result = findUser()
if(!result.isSuccess()) return 'Not found'
const user = result.getData()
return user.name
}
console.log(userExists()) // it works!
The sample code is here in this playground
谁能帮我理解一下?
这里问题的核心是Success
和Notifier
在结构上都是一样的,所以TypeScript认为如果后面的if语句有机会returning true
对于其中一个 类,它 return 对两者都是正确的:
if(result.isFailure())
因此,此块之后的 result
值类型被自动推断为 never
。
我创建了一个示例来说明这一点 here.
通过在两个 类 之一中引入唯一 field/method 或添加私有 field/method.
可以轻松解决此问题以上说明将解决您遇到的问题,并可用于描述 if(result.isFailure())
语句的行为。但是,另一方面,if(result.isSuccess())
似乎仍然适用于您的原始代码。这样做的原因是因为您按照嵌套泛型类型分配给扩展 类:
extends ResultAbstract<F, S>
对比 extends ResultAbstract<E, _>
鉴于上述情况,类 可以被认为是不同的,但是 TypeScript 仍然认为 Success
和 Notifier
与 is
谓词相同告诉它 Success
可以是 Notifier
(通过 isFailure
方法)并且 Notifier
可以是 Success
(通过 isSuccess
方法)。因此,您需要做的就是删除 isSuccess
或 isFailure
方法,代码将开始工作,因为它会告诉 TypeScript 它只能是一个。
因为你的代码只使用了两个类,这将是最合适的解决方案。