在打字稿中缩小条件类型而无判别

时间:2018-09-20 16:39:36

标签: typescript

我有一个简单的api调用函数,该函数可获取数据并根据参数标志有条件地映射结果。

作为获取结果,我得到的是条件类型或联合类型,但是我不能在这里使用类型保护,因为这两种类型不共享公共的可区分属性,因此我应该对其进行区分基于外部类型。我怎样才能告诉打字稿这两种类型取决于另一种类型?是否需要添加另一层间接寻址?

public static Spanned stripHtml(String html) {
            if (!TextUtils.isEmpty(html)) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    return Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT);
                } else {
                    return Html.fromHtml(html);
                }
            }
            return null;
        }

我能够使用它来进行类型转换,并通过函数调用获得正确的返回类型,但这很麻烦,我认为这不是正确的方法

text_view.setText(stripHtml(htmlText));

为条件类型添加类型别名有助于摆脱缺少呼叫签名错误的问题,但我却得到了不匹配的类型错误

// prerequisites
declare function apiService(...a: any[]): any;
declare function mapExternalFoo(x: IExternalFoo): IFoo;
declare function mapExternalBar(x: IExternalBar): IBar;
interface IFoo { a: any; }
interface IBar { b: any; }
interface IExternalFoo { c: any; }
interface IExternalBar { d: any; }

interface IProps<T extends boolean> {
  isExtended?: T;
}

// I want it to look like this, but it fails with a compile error

function fetchData<T extends boolean>(params: IProps<T>): Promise<T extends true ? IFoo : IBar> {
  return apiService('/user/data', params).then(
    (data: T extends true ? IExternalFoo[] : IExternalBar[]) =>
      // Cannot invoke an expression whose type lacks a call signature. 
      params.isExtended ? data.map(mapExternalFoo) : data.map(mapExternalBar)
  );
}

1 个答案:

答案 0 :(得分:3)

TypeScript并未真正将其控制流分析分布在并集类型上,并且对条件类型的处理甚至更少。在类似I wish的时候,您可以告诉编译器手动遍历表达式的某些可能范围,并且仅当所有此类范围均被视为类型安全时,才能成功。 this,这不存在,很难说这种痛点在可预见的将来是否会变得更好。

鉴于这种不幸的情况,最直接的处理方法可能是在函数实现内使用类型声明(如data as IExternalFoo[]),以及函数签名上的重载(您只需要一个带条件类型的重载签名)。 ..和一个带有工会的实施签名)。这几乎就是您所做的,因此,除了“在这种情况下我也是这么做的”之外,我没有其他建议。条件类型对函数调用者比对函数实现者有用。

所以我唯一的建议是您可以将条件类型保留在重载签名中:

function fetchData<T extends boolean>(params: IProps<T>): Promise<T extends true ? IFoo[] : IBar[]>;
function fetchData(params: IProps<boolean>): Promise<IFoo[] | IBar[]> {
  return apiService('/user/data', params)
    .then(
      (data: IExternalFoo[] | IExternalBar[]) =>
        params.isExtended
          ? (data as IExternalFoo[]).map(mapExternalFoo)
          : (data as IExternalBar[]).map(mapExternalBar)
    );
  }

const promseIFooArray = fetchData({isExtended: true});
const promseIBarArray = fetchData({isExtended: false});
const promiseEitherArray = fetchData({isExtended: Math.random()<0.5});

哦,希望能有所帮助。祝你好运!