ตัวแปรที่ปลอดภัย
การสร้างอาร์เรย์หรือทูเปิลของคีย์จากอินเทอร์เฟซที่มีการตรวจสอบเวลาคอมไพล์ความปลอดภัยต้องใช้ความคิดสร้างสรรค์เล็กน้อย ประเภทจะถูกลบที่ใช้เวลาและประเภทวัตถุ (เรียงลำดับชื่อ) ไม่สามารถแปลงเป็นประเภท tuple (สั่งไม่มีชื่อ) โดยไม่ต้อง resorting เทคนิคที่ไม่ได้รับการสนับสนุน
เปรียบเทียบกับคำตอบอื่น ๆ
ที่นำเสนอที่นี่ทุกสายพันธุ์พิจารณา / เรียกรวบรวมข้อผิดพลาดในกรณีที่ซ้ำกันหรือขาดหายไปรายการ tuple IMyTableกำหนดชนิดของวัตถุอ้างอิงเช่น ตัวอย่างเช่นการประกาศประเภทอาร์เรย์ที่(keyof IMyTable)[]ไม่สามารถตรวจจับข้อผิดพลาดเหล่านี้
นอกจากนี้พวกเขาไม่ต้องการไลบรารีเฉพาะ (ใช้ตัวแปรสุดท้ายts-morphซึ่งฉันจะพิจารณาว่าเป็น Wrapper คอมไพเลอร์ทั่วไป) ปล่อยประเภททูเพิลซึ่งตรงข้ามกับอ็อบเจ็กต์ (เฉพาะโซลูชันแรกเท่านั้นที่สร้างอาร์เรย์) หรือประเภทอาร์เรย์แบบกว้าง (เปรียบเทียบกับเหล่านี้ คำตอบ ) และสุดท้ายการเรียนไม่จำเป็นต้อง
ตัวแปร 1: อาร์เรย์ที่พิมพ์ง่าย
function createKeys(keyRecord: Record<keyof IMyTable, any>): (keyof IMyTable)[] {
return Object.keys(keyRecord) as any
}
const keys = createKeys({ isDeleted: 1, createdAt: 1, title: 1, id: 1 })
++-คู่มือที่ง่ายที่สุดพร้อม-อาร์เรย์การเติมข้อความอัตโนมัติไม่มีทูเพิล
สนามเด็กเล่น
ถ้าคุณทำไม่ได้เช่นการสร้างบันทึกดูที่ทางเลือกนี้ด้วยSetและประเภทการยืนยัน
ตัวแปร 2: Tuple พร้อมฟังก์ชันตัวช่วย
function createKeys<T extends readonly (keyof IMyTable)[] | [keyof IMyTable]>(
t: T & CheckMissing<T, IMyTable> & CheckDuplicate<T>): T {
return t
}
++-คู่มือtuple พร้อมการเติมอัตโนมัติ+-ขั้นสูงและประเภทที่ซับซ้อนมากขึ้น
สนามเด็กเล่น
คำอธิบาย
createKeysทำการตรวจสอบเวลาคอมไพล์โดยการรวมประเภทพารามิเตอร์ของฟังก์ชันเข้ากับประเภทการยืนยันเพิ่มเติมซึ่งทำให้เกิดข้อผิดพลาดสำหรับอินพุตที่ไม่เหมาะสม (keyof IMyTable)[] | [keyof IMyTable]เป็นวิธี"มนต์ดำ"เพื่อบังคับให้อนุมานทูเพิลแทนที่จะเป็นอาร์เรย์จากด้านแคลลี หรือคุณสามารถใช้การยืนยัน const /as constจากฝั่งผู้โทร
CheckMissingตรวจสอบหากTพลาดคีย์จากU:
type CheckMissing<T extends readonly any[], U extends Record<string, any>> = {
[K in keyof U]: K extends T[number] ? never : K
}[keyof U] extends never ? T : T & "Error: missing keys"
type T1 = CheckMissing<["p1"], {p1:any, p2:any}>
type T2 = CheckMissing<["p1", "p2"], { p1: any, p2: any }>
หมายเหตุ: T & "Error: missing keys"เป็นเพียงข้อผิดพลาด IDE ที่ดี neverนอกจากนี้คุณยังสามารถเขียน CheckDuplicatesตรวจสอบรายการทูเปิลสองครั้ง:
type CheckDuplicate<T extends readonly any[]> = {
[P1 in keyof T]: "_flag_" extends
{ [P2 in keyof T]: P2 extends P1 ? never :
T[P2] extends T[P1] ? "_flag_" : never }[keyof T] ?
[T[P1], "Error: duplicate"] : T[P1]
}
type T3 = CheckDuplicate<[1, 2, 3]>
type T4 = CheckDuplicate<[1, 2, 1]>
หมายเหตุ: ข้อมูลเพิ่มเติมเกี่ยวกับการตรวจสอบไอเท็มที่ไม่ซ้ำกันใน tuples อยู่ในโพสต์นี้ ด้วยTS 4.1เราสามารถตั้งชื่อคีย์ที่หายไปในสตริงข้อผิดพลาดได้ - ลองดูที่Playgroundนี้
ตัวแปร 3: ชนิดเรียกซ้ำ
ด้วยเวอร์ชัน 4.1 TypeScript สนับสนุนประเภทการเรียกซ้ำตามเงื่อนไขอย่างเป็นทางการซึ่งสามารถใช้ได้ที่นี่เช่นกัน แม้ว่าการคำนวณประเภทจะมีราคาแพงเนื่องจากความซับซ้อนร่วมกัน - ประสิทธิภาพลดลงอย่างมากสำหรับรายการมากกว่า 5-6 รายการ ฉันแสดงรายการทางเลือกนี้เพื่อความสมบูรณ์ ( Playground ):
type Prepend<T, U extends any[]> = [T, ...U]
type Keys<T extends Record<string, any>> = Keys_<T, []>
type Keys_<T extends Record<string, any>, U extends PropertyKey[]> =
{
[P in keyof T]: {} extends Omit<T, P> ? [P] : Prepend<P, Keys_<Omit<T, P>, U>>
}[keyof T]
const t1: Keys<IMyTable> = ["createdAt", "isDeleted", "id", "title"]
++-คู่มือtuple พร้อมการเติมอัตโนมัติ+ไม่มีตัวช่วย--ประสิทธิภาพของฟังก์ชัน
ตัวแปร 4: Code generator / TS compiler API
ts-morphถูกเลือกที่นี่เนื่องจากเป็นทางเลือกของ wrapper ที่ง่ายกว่าเล็กน้อยสำหรับTS คอมไพเลอร์ APIดั้งเดิม แน่นอนคุณยังสามารถใช้ API ของคอมไพเลอร์ได้โดยตรง ลองดูรหัสเครื่องกำเนิดไฟฟ้า:
import {Project, VariableDeclarationKind, InterfaceDeclaration } from "ts-morph";
const project = new Project();
const sourceFile = project.addSourceFileAtPath("./src/IMyTable.ts");
const destFile = project.createSourceFile("./src/generated/IMyTable-keys.ts", "", {
overwrite: true
});
function createKeys(node: InterfaceDeclaration) {
const allKeys = node.getProperties().map(p => p.getName());
destFile.addVariableStatement({
declarationKind: VariableDeclarationKind.Const,
declarations: [{
name: "keys",
initializer: writer =>
writer.write(`${JSON.stringify(allKeys)} as const`)
}]
});
}
createKeys(sourceFile.getInterface("IMyTable")!);
destFile.saveSync();
หลังจากที่เรารวบรวมและเรียกใช้ไฟล์นี้tsc && node dist/mybuildstep.jsแล้วไฟล์ที่./src/generated/IMyTable-keys.tsมีเนื้อหาต่อไปนี้จะถูกสร้างขึ้น:
const keys = ["id","title","createdAt","isDeleted"] as const;
+โซลูชันที่สร้างขึ้นโดยอัตโนมัติ+สามารถปรับขนาดได้สำหรับคุณสมบัติหลายอย่าง+ไม่มีฟังก์ชันตัวช่วย+tuple -extra build-step -ต้องการความคุ้นเคยกับ compiler API