Geo*_*ids 9 authorization typechecking observable typescript typeguards
我一直在开发一个身份验证服务,该服务使用 rxjs 行为主题来存储最后检索到的身份验证对象,并在该对象已过期(或根本尚未获取)时触发重新获取。
我的问题是关于 TypeScript 类型检查器。我已经编写了断言的类型保护程序isNotUndefined- 嗯,正是您所期望的。
export function isNotUndefined<T>(input: T | undefined): input is T {
return input !== undefined;
}
Run Code Online (Sandbox Code Playgroud)
我已经不得不编写上面的 typeguard 而不是能够依赖auth !== undefined. 我现在无法理解为什么在authGetter$下面代码的管道中,管道中值的类型没有减少到Auth第一个过滤器之后。相反,类型仍然是Auth | undefined,并且需要第二个过滤器仅带有类型保护才能将类型缩小到仅Auth。
总而言之,为什么我需要第二个过滤器将类型缩小到仅Auth?此外,因为我是自己编码,没有人审查它,所以我非常感谢任何人指出他们认识到的“代码味道”(并提供有关如何处理的建议)。
export default class AuthService {
private static lastAuth$ = new BehaviorSubject<Auth | undefined>(undefined);
private static authGetter$ = AuthService.lastAuth$.pipe(
filter(auth => {
if (isNotUndefined(auth) && auth.expiry > new Date()) {
return true ; // identical resulting type with "return isNotUndefined(auth);"
} else {
// retry if auth doesn't exist or is expired
AuthService.authorise().then(newAuth =>
AuthService.lastAuth$.next(newAuth)
);
return false;
}
}),
tap(v => {}), // typechecker says "(parameter) v: Auth | undefined"
filter(isNotUndefined),
tap(v => {}) // typechecker says "(parameter) v: Auth"
);
static getAuth$(): Observable<Auth> {
return this.authGetter$.pipe(first());
}
private static async authorise(): Promise<Auth> {
// auth code goes here (irrelevant for this question)...
// typecast dummy return to make typechecker happy
return Promise.resolve(<Auth>{});
}
}
Run Code Online (Sandbox Code Playgroud)
我附上了我的代码的照片,并以漂亮的语法突出显示,以便您轻松查看:)
jca*_*alz 12
用户定义的类型保护函数至少目前是严格由用户定义的。它们不是由编译器自动推断的。如果您希望boolean返回函数充当具有类型谓词返回类型的类型保护,则需要将其显式注释为:
const doesNotPropagate = <T>(x: T | undefined) => isNotUndefined(x);
// const doesNotPropagate: <T>(x: T | undefined) => boolean
Run Code Online (Sandbox Code Playgroud)
该函数的doesNotPropagate()行为与运行时相同isNotUndefined(),但编译器不再将其视为类型保护,因此如果将其用作过滤器,则不会undefined在编译器中消除它。
GitHub 上有多个与此相关的问题;当前未解决的跟踪传播/流动类型保护签名的问题是microsoft/TypeScript#16069(或可能是microsoft/TypeScript#10734)。但看起来这里没有太多变化,所以暂时我们需要按原样处理语言。
这是一个玩具示例,可以用来探索处理此问题的不同可能方法,因为您问题中的示例代码并不构成适合放入独立 IDE 的最小可重现示例。您应该能够使它们适应您自己的代码。
假设我们有一个otype 的值Observable<string | undefined>。然后这有效:
const a = o.pipe(filter(isNotUndefined)); // Observable<string>
Run Code Online (Sandbox Code Playgroud)
但这并不是因为上面列出的原因...类型保护签名不会传播:
const b = o.pipe(filter(x => isNotUndefined(x))); // Observable<string | undefined>
Run Code Online (Sandbox Code Playgroud)
如果我们像这样手动注释箭头函数,我们可以重新获得类型保护签名和行为:
const c = o.pipe(filter((x): x is string => isNotUndefined(x))); // Observable<string>;
Run Code Online (Sandbox Code Playgroud)
如果您愿意,您可以从此执行额外的过滤逻辑:
const d = o.pipe(filter((x): x is string => isNotUndefined(x) && x.length > 3));
// Observable<string>;
Run Code Online (Sandbox Code Playgroud)
这里过滤器检查字符串是否已定义并且其长度是否大于 3。
请注意,从技术上讲,这不是一个行为良好的用户定义类型保护,因为它们倾向于将false结果视为意味着输入范围缩小以排除受保护的类型:
function badGuard(x: string | undefined): x is string {
return x !== undefined && x.length > 3;
}
const x = Math.random() < 0.5 ? "a" : undefined;
if (!badGuard(x)) {
x; // narrowed to undefined, but could well be string here, oops
}
Run Code Online (Sandbox Code Playgroud)
在这里,如果badGuard(x)返回true,您就知道x是string。但如果badGuard(x)return false,你不知道那x是undefined......但这就是编译器的想法。
确实,在您的代码中,您并没有真正处理过滤器返回的情况false(我猜后续的管道参数只是不触发?),所以您实际上不必太担心这一点。尽管如此,最好将代码重构为一个正确的类型保护,然后是执行额外逻辑的非类型保护过滤器:
const e = o.pipe(filter(isNotUndefined), filter(x => x.length > 3)); // Observable<string>;
Run Code Online (Sandbox Code Playgroud)
这在运行时应该达到相同的结果,但这里第一个过滤器正确地从Observable<string | undefined>to缩小范围Observable<string>,第二个过滤器保留Observable<string>(并且x回调中的 是 a string) 并执行根据长度过滤的额外逻辑。
这具有不需要类型注释的额外好处,因为您不会尝试在任何地方传播类型保护签名。所以这可能是我推荐的方法。
好的,希望有帮助;祝你好运!
| 归档时间: |
|
| 查看次数: |
4233 次 |
| 最近记录: |