Typescript 如何对联合类型的两个分支执行运行时检查?

Typescript 如何对联合类型的两个分支执行运行时检查?,typescript,Typescript,我有一个以下类型的变量: const data: { error: string; } | { property1: string; }; (这是一个外部API的响应,所以我不能更改它。) 现在我想检查两种可能的类型data中的哪一种是正确的。我试过这个: if (typeof data.error !== 'undefined') { } 不幸的是,TypeScript随后抱怨说 Property `error` does not exist on type `{ property1: s

我有一个以下类型的变量:

const data: { error: string; } | { property1: string; };
(这是一个外部API的响应,所以我不能更改它。)

现在我想检查两种可能的类型
data
中的哪一种是正确的。我试过这个:

if (typeof data.error !== 'undefined') { }
不幸的是,TypeScript随后抱怨说

Property `error` does not exist on type `{ property1: string }`.

如何让TypeScript知道它是哪种可能的类型?

我能想到的一个简单方法是使用一种带有属性的类型,而不是联合类型。像这样:

interface ApiResponse = {
    error?: string;
    property1?: string;
}

const errorData: ApiResponse = { error: 'error data' };
const okData: ApiResponse = { property1: 'ok data' };

console.log(typeof errorData.error !== 'undefined'); // true
console.log(typeof okData.error !== 'undefined'); // false

// or even cleaner:

console.log(errorData.error); // truthy
console.log(okData.error); // falsey

console.log(!!errorData.error); // true
console.log(!!okData.error); // false

这是我目前正在使用的解决方法。作为一种变通方法,这很好,但我更愿意正确使用TypeScript的类型系统:

const data: any = /* API response */;

if (data.error) {
  const errorData: { error: string; } = data;
  // Do whatever
}

const actualData: { property1: string; } = data;

这正是TypeScript失败的原因。您可以告诉TypeScript如何区分联合体的两个组成部分:

function dataHasError(data: { error: string; } | { property1: string; }): data is { error: string; } {
  return 'error' in data;
}
然后它将按照您在
if
/
else
子句中所期望的方式缩小类型:

const data: { error: string; } | { property1: string; } = /* API response */;

if (dataHasError(data)) {
  console.log(data.error); // okay
} else {
  console.log(data.property1); // okay
}

如果您经常发现自己需要通过检查是否存在密钥来进行此类型缩小,则可以在库中放置以下类型保护:

function hasKey<K extends string>(obj: any, key: K): obj is Record<K, any> {
  return key in obj;
}

相关说明:TypeScript有一个自动将obj中的
键解释为类型保护的功能,因此以下功能可以正常工作:

if ('error' in data)) {
  console.log(data.error); // would be okay, currently error
} else {
  console.log(data.property1); // would be okay, currently error
}
目前(从TypeScript v2.5开始),这不是语言的一部分,因此用户定义的类型保护是目前最好的答案



希望有帮助;祝你好运

但是在这种情况下,我必须在我想使用这些属性的任何地方添加额外的检查,即使我在第一次检查
data.error
是否存在后知道对象上应该存在哪些属性。我现在已经解决了这个问题,将其保留为
any
类型,然后在检查后将其重新分配给一个正确类型的对象,但这仍然很麻烦。我不知道有用户定义的类型保护,但这些当然可以解决我的问题。(我正在关注这个问题——希望它能在某个时候出现。)谢谢!
if ('error' in data)) {
  console.log(data.error); // would be okay, currently error
} else {
  console.log(data.property1); // would be okay, currently error
}