【问题标题】:How to type different data coming from server如何键入来自服务器的不同数据
【发布时间】:2022-01-09 11:31:00
【问题描述】:

我有一个对象将具有字段products,该字段将填充不同端点返回的响应,每个端点返回一种产品类型。类似:

const products = {
  // each of this kind of products comes from endpoint response
  someProduct: {},
  someOtherProduct: {}
  anotherProduct: {}
}

所有产品类型都有一些共同的属性,例如名称、描述、价格。但它们也具有仅适用于一种产品的特定属性。在某些情况下,我需要遍历所有产品以呈现这些常见属性的列表,但有时我需要为每种类型的产品呈现特定数据。

如果这个product 对象包含不同的产品类型,每个产品类型都有不同的属性并且来自外部数据源(服务器),我该如何输入?

编辑 1:产品类型

每个产品都有一个属性type,用于标识每种产品类型

编辑 2:选定的产品类型

当我呈现列表中的所有产品时,用户可以单击其中任何一个并将所选产品存储在一个变量中...这样我就可以使用该所选产品打印特定产品详细信息...如何键入这个选定的产品,如果它可以是任何类型的产品?

【问题讨论】:

  • 你可以添加一个type属性来指明是哪一个,然后根据type用if else语句检查属性或者...
  • 你在谈论类型吗?我不明白你的意思......你能用一个具体的例子来回答......
  • 产品类型的数量是否有限?您可以只键入每个单独的 API 响应,然后将 products 对象设为具有这些响应的联合类型的记录。你需要类型警卫(即const objIsMyProductType(obj: any): obj is MyProductType => return obj.type === 'MyProductType' 在你遍历时做事。我在这里遗漏了什么吗?
  • 您是要强类型化所有可能的产品,还是只定义一些已知的产品(并且只定义其他产品的基本属性)?
  • @BrendanBond 我的产品类型有限(六种),如果您能发布更完整的答案以便我了解整个上下文,我将不胜感激。

标签: javascript reactjs typescript


【解决方案1】:

更新

您还可以在 CodeSandbox 中查看一个工作示例:使用数组和选定产品,以及所有样式:https://codesandbox.io/s/condescending-sea-53xh8?file=/src/App.tsx

原创

你可以这样做:

在这种情况下,我们为所有公共属性定义一个 BaseProduct。我们扩展此产品接口以定义其他每个接口。

然后,我们定义一个类型Product,它可以是前面定义的任何类型的Product 接口。

最后,我们可以定义一个 Product 数组(如果您的服务器使用数组回答)或一个对象,该对象可以将任何字符串作为 keyProduct 作为值。

interface BaseProduct {
  name: string;
  description: string;
  price: number;
  type: string;
}

interface SomeProduct extends BaseProduct {
  genericAttrOne: string;
  genericAttrTwo: string;
}

interface OtherProduct extends BaseProduct {
  anAttributeOne: string;
  anAttributeTwo: string;
}

interface AnotherProduct extends BaseProduct {
  anotherAttributeOne: string;
  anotherAttributeTwo: string;
}

type Product = SomeProduct | OtherProduct | AnotherProduct;

const productsAsArray: Product[] = [];
const productsAsObject: { [key: string]: Product }: {};

【讨论】:

  • 这里的情况是有时我会遍历所有产品类型(你的答案中的 productsAsArray),然后我会渲染它,然后用户点击一些产品,我把它当作productSelected .如果它可以是任何类型的产品,我应该如何输入这个产品?对于这个 selectedProduct,我将访问特定于产品的属性...
  • 在这种情况下:您的selectedProduct 将输入Product,现在关于迭代和产品特定属性,您将需要额外的条件或不同的组件。
  • @CristianFlórez 我刚刚更新了一个代码框链接,所以您可以查看如何操作 :)
  • 在您的示例中,特别是在 ProductDetail 文件中,SomeDetailsOtherDetailsAnotherDetails 正在引发类型错误...Property 'anotherAttributeTwo' does not exist on type 'Product'. 这个错误发生在每个特定的产品组件中跨度>
  • 是的,我注意到(我已经编辑了沙箱),当您想专注于每种类型时,可以为每个细节使用特定类型。您现在可以检查沙箱,它有 SomeDetails(带有Product)作为 Prop 并给出错误。而且,我们有 SomeDetailsTwo(带有SomeProduct)并且像魅力一样工作:)
【解决方案2】:

这是一个强输入您描述的响应的示例:

TS Playground link

// Start with the base product, defining the common properties
type Product<
  T extends string = string,
  P = Record<never, never>,
> = P & {
  type: T;
  name: string;
  description: string;
  price: number;
};

// Then define each product's specific type and properties
type SomeProduct = Product<'some', {
  someValue1: string;
  someValue2: number;
}>;

// For every product
type AnotherProduct = Product<'another', {
  anotherValue1: string;
  anotherValue2: number;
}>;

// Then assign them all to properties in an object of known products
type KnownProducts = {
  some: SomeProduct;
  another: AnotherProduct;
  // etc...
};

// Create a type for known products + any generic product with common properties
type AllProducts = KnownProducts & { [key: string]: Product };

declare const products: AllProducts;

products.some.name // ok
products.another.name // ok

products.some.type // "some"
products.some.someValue1 // ok
products.some.anotherValue1 // property doesn't exist

products.another.type // "another"
products.another.someValue1 // property doesn't exist
products.another.anotherValue1 // ok

// All other products will only have the commoon properties
products.mysteryProduct.name; // ok


// Narrowing a product type:

type KnownProduct = KnownProducts[keyof KnownProducts];
type AnyProduct = KnownProduct | Product;

function doSomethingWithAProduct (product: AnyProduct): void {
  // explicitly handle cases for known products using an assertion, and the reference `p`
  const p = product as KnownProduct;
  switch (p.type) {
    case 'some': {
      p.type // "some"
      p.name // ok
      p.someValue1 // ok
      p.anotherValue1 // Error: Property doesn't exist
      break;
    }
    case 'another': {
      p.type // "another"
      p.name // ok
      p.someValue1 // Error: Property doesn't exist
      p.anotherValue1 // ok
      break;
    }
    // in the default case use the argument `product` (which is still AnyProduct)
    default: {
      product.name;
      break;
    }
  }
}

【讨论】:

  • 这里的情况是有时我会遍历所有产品类型(您的答案中的产品),然后我会渲染它,然后用户点击一些产品,我将其作为productSelected .如果productSelected 可以是任何类型的产品,我应该如何输入它?对于这个 selectedProduct,我将访问特定于产品的属性...
  • 1.现在你问了一个与你原来的问题不同的问题。 2.每个产品的type属性的值是否与产品对象中其键名匹配?
  • 是的,我发布了另一个与此相关的question...如果您能在此处回复以获取更多上下文信息,我将不胜感激......
  • 我已经更新了我的答案以满足您的额外要求。
【解决方案3】:

根据您对两个(非常好的)答案的后续 cmets 判断,您需要在运行时(即当用户选择产品时)输入未确定的产品类型,您需要使用 type guards

// here’s the type guard for one type, you’ll need one for all six; we’re checking against your `type` property but it could be anything
const isMyProductType = (obj: any): obj is MyProductType => obj.type === “myProductType”;

const onClick = (selectedProduct) => {
  if (isMyProductType(selectedProduct)) {
    // TS now will treat selectedProduct as an object of MyProductType
    // do stuff 
  } else if (/* another type guard etc … */) {
    // etc
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    • 2013-11-28
    • 1970-01-01
    • 1970-01-01
    • 2019-10-09
    • 1970-01-01
    相关资源
    最近更新 更多