我们有一个API响应,我们希望改进我们的类型,我将简化这个示例.假设API返回的内容如下所示,其中有一个值类型和一个关联值.

Note:遗憾的是,这是一个现有的API,我们只是试图使用它在当前形状中提供的东西.

{
  meta: {
    id: string;
    name: string;
    valueType: 'cat' | 'bird'
  }
  value: { paws: number } | { wings: number }
}

在此响应中,类型valueTypevalue之间实际上存在直接关系.因此,为了try 并改进对API响应的类型支持,我try 了如下内容.

type MetaType = {
  id: string;
  name: string;
};

type APIResponseType =
  | {
      meta: MetaType & {
        valueType: 'cat';
      };
      value: { paws: number };
    }
  | {
      meta: MetaType & {
        valueType: 'bird';
      };
      value: { wings: number };
    };

const sampleResponse: APIResponseType[] = [
  {
    meta: {
      id: '123',
      name: 'Garfield',
      valueType: 'cat',
    },
    value: { paws: 4 },
  },
  {
    meta: {
      id: '123',
      name: 'Woody',
      valueType: 'bird',
    },
    value: { wings: 2 },
  },
];

到目前为止,这种方法工作得很好,当静态输入数据时,类型确保了数据的正确形状.如果您try 在第一个对象中将valueType从cat更改为bird,则会导致类型错误.

现在,当我try 以动态方式与此数据交互并try 使用类型保护时,出现了问题.

sampleResponse.forEach((animal) => {
  if (animal.meta.valueType === 'cat') {
    console.log(animal.value.paws);
  }
});

// TypeScript see's the type for value as this:
(property) value: { paws: number; } | { wings: number; }

因此,在这种情况下,尽管类型最初似乎可以工作,但它无法使用类型保护起到预期的作用,这仍然意味着我们需要进行强制转换.

那么,有没有办法通过另一种打字方法实现这一功能呢?

推荐答案

TypeScrip似乎很难从内部对象缩小父对象的范围.我希望有一种优雅的方法来处理这一问题,但正如@Kelly在他们的答案中指出的那样,这里可能需要打字警卫.(这一事实让我大吃一惊,但似乎是真的.)

但是,为了防止您对every种可能的类型使用不同的函数,您可以使用一个泛型函数来为您执行判断:

function checkResponseType<
  T extends APIResponseType['meta']['valueType']
>(
  res: APIResponseType,
  type: T
): res is Extract<APIResponseType, { meta: { valueType: T }}> {
  return res.meta.valueType === type
}

在这里,我们要判断的类型是T.我们用use Extract来将并集过滤到只有匹配项.

然后,使用感觉和外观都很不错.

sampleResponse.forEach((animal) => {
  if (checkResponseType(animal, 'cat')) {
    console.log(animal.value.paws); // fine
  }
});

See playground

Typescript相关问答推荐

相交TypScript中的对象和接口

发出与HTML多姆事件重叠的Angular事件

交叉点对数字和布尔的处理方式不同

"@ clerker/nextjs/server没有名为clerkMiddleware的导入成员'

如何访问Content UI的DatePicker/中的sx props of year picker?'<>

React typescribe Jest调用函数

在NextJS中获得Spotify Oauth,但不工作'

将对象属性转换为InstanceType或原样的强类型方法

异步动态生成Playwright测试,显示未找到测试

如何使默认导出与CommonJS兼容

角效用函数的类型推断

垫表页脚角v15

如何在Typescript 中组合unions ?

使用动态输出类型和泛型可重用接口提取对象属性

专用路由组件不能从挂钩推断类型

在TypeScript中扩展重载方法时出现问题

抽象类对派生类强制不同的构造函数签名

TypeScript:方法获胜';t接受较窄类型作为参数

Typescript泛型-键入对象时保持推理

断言同级为true或抛出错误的Typescript函数