javascript - TypeScript กำหนดค่าอ็อบเจ็กต์ด้วยคุณสมบัติที่ไม่ซ้ำกัน
ฉันเขียนอินเทอร์เฟซง่ายๆ แบบนั้น:
interface IProduct {
id: number;
name: string;
price: number;
description?: string;
}
ตอนนี้ฉันต้องการให้ id เป็นเอกลักษณ์ใน ReadonlyArray ดังนั้นเมื่อฉันสร้างสินค้าไม่กี่ชิ้น ฉันต้องการป้องกันการเพิ่มวัตถุที่มี id เดียวกัน อาร์เรย์ที่มีสินค้าจะถูกสร้างเพียงครั้งเดียว ในไฟล์ และจะไม่ถูกแก้ไข
คุณมีไอเดียสำหรับเรื่องนั้นหรือไม่? JS Set จะเป็นทางออกที่ดี แต่ฉันไม่สามารถเพิ่มตัวเปรียบเทียบของตัวเองให้กับมันได้ โปรดอย่าให้วิธีแก้ปัญหาที่ต้องใช้กรอบงานเพิ่มเติม ฯลฯ
คำตอบ #1
ตัวอย่างนี้ใช้ระบบประเภทเพื่อยืนยันแบบสถิติว่ามีค่าซ้ำหรือไม่
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'))
สนามเด็กเล่น
การตรวจสอบ - วนซ้ำแบบเรียกตัวเองผ่านสินค้าทั้งหมดที่ส่งเข้าฟังก์ชัน หาก product[id] มีอยู่แล้วในประเภทตัวสะสม - ให้แทนที่คุณสมบัติ id ด้วย never มิฉะนั้นเพียงแค่เพิ่มสินค้าลงในตัวสะสม
โปรดดูความคิดเห็น #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
สนามเด็กเล่น
ถ้าคุณสนใจในการตรวจสอบแบบสถิต คุณสามารถดูบทความของฉันได้
คำตอบ #2
คุณต้องการการรับประกันในเวลาคอมไพล์สำหรับสิ่งนี้หรือไม่? ฉันสงสัยว่านี่จะเป็นไปได้ใน 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. ผมคิดว่าคุณสามารถลองทำให้คลาสของคุณใช้อินเทอร์เฟซนี้ได้ ถ้านั่นคือสิ่งที่คุณต้องการ
