使用 Promise 的 TypeScript 异步类型保护

Posted

技术标签:

【中文标题】使用 Promise 的 TypeScript 异步类型保护【英文标题】:TypeScript async type guard using Promise 【发布时间】:2018-01-01 20:53:28 【问题描述】:

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

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

任何帮助将不胜感激!

【问题讨论】:

它似乎还不是一个功能。也许你可以submit the idea? 当使用 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);
);

【讨论】:

以上是关于使用 Promise 的 TypeScript 异步类型保护的主要内容,如果未能解决你的问题,请参考以下文章

使用 typescript 在 Redux thunk 中返回一个 Promise

使用 angularJS 和 Typescript 的 Promise

使用 Angular 1 应用程序在 Typescript 中管理 ES6/2015 Promise

使用 typescript 中 Promise 中的值设置 React.FC 状态

Typescript:Promise 的子类/扩展:不引用 Promise 兼容的构造函数值

使用 Angular Promise 进行 Jasmine 测试无法使用 TypeScript 解决