typescript 如何使用泛型和条件类型缩小API响应类型?

w6mmgewl  于 2023-05-19  发布在  TypeScript
关注(0)|答案(1)|浏览(115)

我正在向API端点发送一个请求,它的响应根据响应中名为productType的字段而不同。例如,API可以返回以下响应:

// response 1
{
    productType: 'Product1',
    product: 'Some product information'
}

// response 2
{
    productType: 'Product2',
    product: {moreNestedFields: 'Some product information'}
}

我希望能够根据productType在我的客户端代码中运行不同的逻辑(缩小类型)。
以下是我的简化尝试:

type Product = 'Product1' | 'Product2';
type MyResponse<TProduct extends Product> = {
  product: TProduct extends 'Product1' ? 'SomeType' : 'SomeOtherType';
  productType: TProduct;
};

function getResponse<T extends Product>() {
  // This is accually some fetch request
  return {} as MyResponse<T>;
}

const response = getResponse();

if (response.productType === 'Product1') {
   //  myProduct is not type narrowed?
   // I am expecting it to only have 'SomeType' but its type is <'SomeType' | 'SomeOtherType'>
  const myProduct = response.product;                      
  // do something with myProduct
} else {
  // ..
}

我错过了什么?我如何才能做到这一点?

sdnqo3pr

sdnqo3pr1#

您应该使用区分联合类型而不是泛型。更新MyResponse类型:

type Product = 'Product1' | 'Product2';
type MyResponse =
  | {
      productType: Extract<Product, 'Product1'>;
      product: 'SomeType';
    }
  | {
      productType: Extract<Product, 'Product2'>;
      product: 'SomeOtherType';
    };

注:Extract是为了避免打字错误而增加的。
Playground
如果所有类型的响应都有公共字段,则可以将它们放在单独的类型中,并将它们与并集相交:

type CommonFields = {
  status: string;
};

type MyResponse = CommonFields &
  (
    | {
        productType: Extract<Product, 'Product1'>;
        product: 'SomeType';
      }
    | {
        productType: Extract<Product, 'Product2'>;
        product: 'SomeOtherType';
      }
  );

playground

相关问题