使用Promise的TypeScript异步类型保护

时间:2017-07-26 13:34:01

标签: typescript asynchronous types

我试图定义异步类型保护。我可以同步执行以下操作:

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

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分: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);
});