使用承诺的 TypeScript 异步类型保护



我正在尝试定义一个异步类型保护。我可以同步执行以下操作:

class Foo {
public type = 'Foo';
}
// Sync type guard:
function isFoo(obj: any): obj is Foo {
return typeof obj.type !== 'undefined' && obj.type === 'Foo';
}
function useFoo(foo: Foo): void {
alert(`It's a Foo!`);
}
const a: object = new Foo();
if (isFoo(a)) useFoo(a);

但我不确定如何做同样的异步。这是我尝试过的:

class Bar {
public getType = () => new Promise(resolve => {
setTimeout(() => resolve('Bar'), 1000);
});
}
// Async type guard:
async function isBar(obj: any): Promise<obj is Bar> {
if (typeof obj.getType === 'undefined') return false;
const result = await obj.getType();
return result === 'Bar';
}
function useBar(bar: Bar): void {
alert(`It's a Bar!`);
}
const b: object = new Bar();
isBar(b).then(bIsBar => {
if (bIsBar) useBar(b);
});

在这里尝试一下

任何帮助将不胜感激!

不可以,不能从函数的直接范围之外访问受保护的参数。 所以一旦你回报了一个承诺,你就不能再守obj了。 这听起来像是一个简洁的功能创意,正如@Paleo所建议的那样,如果还没有的话,你应该提交它。

不过,这可能无济于事;即使你可以作用域表达类型保护,编译器也可能会再次扩大类型,因为值可能会发生变化:

class Bar {
public getType = () => new Promise(resolve => {
setTimeout(() => resolve('Bar'), 1000);
});
public barProp: string; // added to distinguish structurally from NotBar
}
class NotBar {
public getType = () => new Promise(resolve => {
setTimeout(() => resolve('NotBar'), 1000);
});
public notBarProp: string; // added to distinguish structurally from Bar
}
function useBar(bar: Bar): void {
alert(`It's a Bar!`);
}
function useNotBar(notBar: NotBar): void {
alert(`Nope, not a Bar.`)
}
var b: Bar | NotBar = new Bar();
if (b instanceof Bar) {
useBar(b); // narrowed to Bar, no error
isBar(b).then(bIsBar => {        
useBar(b); // error! widened to Bar | NotBar again
})
}

作为一种可能的解决方法,您可以发明自己的"类型保护"对象并将其传递回去,尽管使用起来并不那么愉快:

type Guarded<Y, N = any> = { matches: true, value: Y } | { matches: false, value: N };
function guarded<Y, N = any>(v: Y | N, matches: boolean): Guarded<Y, N> {
return matches ? { matches: true, value: <Y>v } : { matches: false, value: <N>v };
}
// Async type guard:
async function isBar<N extends { getType?: () => Promise<any> } = any>(obj: Bar | N): Promise<Guarded<Bar, N>> {
if (typeof obj.getType === 'undefined') return guarded(obj, false);
const result = await obj.getType();
return guarded(obj, result === 'Bar');
}
isBar(b).then(bIsBar => {
if (bIsBar.matches) useBar(bIsBar.value);
});
isBar<NotBar>(b).then(bIsBar => {
if (bIsBar.matches) useBar(bIsBar.value); else useNotBar(bIsBar.value);
});

最新更新