2017-07-26 83 views
2

我试图定义一个异步类型守卫。我可以同步做到以下几点:使用Promise的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); 
}); 

Try it here

任何帮助,将不胜感激!

+0

看起来它不是一个功能呢。也许你可以[提交想法](https://github.com/Microsoft/TypeScript/issues)? – Paleo

+0

当使用tsc 2.0.9和节点7.7.3定位es6时,这似乎工作正常。 –

回答

1

不,您不能从函数的直接范围外访问保护参数。所以一旦你回复了一个承诺,你就无法防范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); 
});