簡體   English   中英

具有唯一屬性的 TypeScript 對象集

[英]TypeScript object set with unique property

我簡單地寫了這樣的接口:

interface IProduct {
  id: number;
  name: string;
  price: number;
  description?: string;
}

現在我想id在 ReadonlyArray 中是唯一的。 因此,當創建一些產品時,我想防止添加具有相同 id 的對象。 包含產品的數組將在文件中創建一次,不會被修改。

你有什么想法嗎? JS Set 將是很好的解決方案,但我無法向它們添加自己的比較器。 請不要提供需要額外框架等的解決方案。

此示例使用類型系統靜態驗證是否存在重復項。


interface IProduct<Id extends number> {
    id: Id
    name: string;
}

const product = <Id extends number>(id: Id, name: string) => ({ id, name })

type Validation<
    Products extends IProduct<number>[],
    Accumulator extends IProduct<number>[] = []>
    =
    (Products extends []
        // #1 Last call
        ? Accumulator
        // #2 All calls but last
        : (Products extends [infer Head, ...infer Tail]
            ? (Head extends IProduct<number>
                // #3 Check whether [id] property already exists in our accumulator 
                ? (Head['id'] extends Accumulator[number]['id']
                    ? (Tail extends IProduct<number>[]
                        // #4 [id] property is a duplicate, hence we need to replace it with [never] in order to trigger the error
                        ? Validation<Tail, [...Accumulator, { id: never, name: Head['name'] }]>
                        : never)
                    // #5 [id] is not a duplicate, hence we can add to our accumulator whole product
                    : (Tail extends IProduct<number>[]
                        ? Validation<Tail, [...Accumulator, Head]>
                        : never)
                )
                : never)
            : never)
    )

type Ok = Validation<[{ id: 1, name: '1' }, { id: 2, name: '2' }]>
type Fail = Validation<[{ id: 1, name: '1' }, { id: 1, name: '2' }]> // id:never

const builder = <
    Product extends IProduct<number>,
    Products extends Product[]
>(...products: [...Products] & Validation<Products>) => products

builder(product(1, 'John'), product(2, 'Doe'))

操場

Validation - 遞歸遍歷所有傳遞給函數產品。 如果累加器類型中已經存在product[id] - 用never替換id屬性,否則只需將 product 添加到累加器。

請參閱評論#1 , #2 ....

如果您不想使用rest運算符,請考慮以下示例:


interface IProduct<Id extends number> {
    id: Id
    name: string;
}

const product = <Id extends number>(id: Id, name: string) => ({ id, name })

type Validation<
    Products extends IProduct<number>[],
    Accumulator extends IProduct<number>[] = []>
    =
    (Products extends []
        // #1 Last call
        ? Accumulator
        // #2 All calls but last
        : (Products extends [infer Head, ...infer Tail]
            ? (Head extends IProduct<number>
                // #3 Check whether [id] property already exists in our accumulator 
                ? (Head['id'] extends Accumulator[number]['id']
                    ? (Tail extends IProduct<number>[]
                        // #4 [id] property is a duplicate, hence we need to replace it with [never] in order to trigger the error
                        ? Validation<Tail, [...Accumulator, { id: never, name: Head['name'] }]>
                        : 1)
                    // #5 [id] is not a duplicate, hence we can add to our accumulator whole product
                    : (Tail extends IProduct<number>[]
                        ? Validation<Tail, [...Accumulator, Head]>
                        : 2)
                )
                : 3)
            : Products)
    )


type Ok = Validation<[{ id: 1, name: '1' }, { id: 2, name: '2' }]>
type Fail = Validation<[{ id: 1, name: '1' }, { id: 1, name: '2' }]> // id:never

const builder = <
    Id extends number,
    Product extends IProduct<Id>,
    Products extends Product[]
>(products: Validation<[...Products]>) => products

builder([product(1, 'John'), product(1, 'John')]) // error

操場

如果您對靜態驗證感興趣,可以查看我的文章

你想要編譯時保證嗎? 我懷疑這在 TypeScript 中是可能的。 編輯:也許在看到其他答案后有可能。

然而,JavaScript 代碼非常簡單:

let products = new Map();

let product = {
  id: 1,
  name: "Foo",
  price: 99,
  description: "Bar",
};

// This will update an existing item if it has the same ID
products.set(product.id, product);

// Alternatively, you can check if one already exists in keep the original item
if (!products.has(product.id)) {
  products.set(product.id, product);
}

您可以使用一組函數將此代碼包裝在您自己的類中:

class ProductList {
  constructor() {
    this.items = new Map();
  }

  add(product) {
    if(!this.items.has(product.id)) {
      this.items.set(product.id, product);
    }
  }

  values() {
    // iterate over values
    // this will respect insertion order
    return this.items.values();
  }

  // any other methods you'd like...
}

如果您需要通過索引進行隨機訪問,您可以在您的類中存儲一個數組,該數組與您的集合保持同步/更新。 這不應該太難編碼。

你提到了ReadonlyArray 我認為你可以嘗試讓你的類實現這個接口,如果這是你想要的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM