Varian yang aman
Membuat array atau tuple kunci dari antarmuka dengan pemeriksaan waktu kompilasi keamanan membutuhkan sedikit kreativitas. Tipe dihapus pada waktu proses dan tipe objek (tidak berurutan, dinamai) tidak dapat diubah menjadi tipe tupel (diurutkan, tidak dinamai) tanpa menggunakan teknik yang tidak didukung .
Perbandingan dengan jawaban lain
Varian yang diusulkan di sini semua mempertimbangkan / memicu kesalahan kompilasi jika ada item tupel duplikat atau hilang dengan jenis objek referensi seperti IMyTable. Misalnya mendeklarasikan tipe array(keyof IMyTable)[] tidak dapat menangkap kesalahan ini.
Selain itu, mereka tidak memerlukan pustaka tertentu (penggunaan varian terakhir ts-morph, yang saya anggap sebagai pembungkus kompiler generik), memancarkan jenis tupel sebagai lawan dari objek (hanya solusi pertama yang membuat larik) atau jenis larik lebar (bandingkan dengan jawaban ini ) dan terakhir tidak perlu kelas .
Varian 1: Array yang diketik sederhana
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 })
++-manual termudah dengan pelengkapan otomatis- larik , tanpa tupel
Tempat bermain
Jika Anda tidak suka membuat record, lihat alternatif with Setdan assertion types ini .
Varian 2: Tuple dengan fungsi helper
function createKeys<T extends readonly (keyof IMyTable)[] | [keyof IMyTable]>(
t: T & CheckMissing<T, IMyTable> & CheckDuplicate<T>): T {
return t
}
++-manual tuple dengan pelengkapan otomatis +-tipe yang lebih canggih dan kompleks
Tempat bermain
Penjelasan
createKeysmelakukan pemeriksaan waktu kompilasi dengan menggabungkan tipe parameter fungsi dengan tipe pernyataan tambahan, yang mengeluarkan kesalahan untuk input yang tidak sesuai. (keyof IMyTable)[] | [keyof IMyTable]adalah cara "sihir hitam" untuk memaksa inferensi tupel alih-alih larik dari sisi callee. Atau, Anda dapat menggunakan pernyataan konstanta /as const dari sisi pemanggil.
CheckMissingcek, jika Tmelewatkan kunci dari 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 }>
Catatan: T & "Error: missing keys"hanya untuk kesalahan IDE yang bagus. Anda juga bisa menulis never. CheckDuplicatesmemeriksa item tupel ganda:
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]>
Catatan: Info lebih lanjut tentang pemeriksaan item unik di tuple ada di posting ini . Dengan TS 4.1 , kami juga dapat memberi nama kunci yang hilang dalam string kesalahan - lihat Playground ini .
Varian 3: Tipe rekursif
Dengan versi 4.1, TypeScript secara resmi mendukung jenis rekursif bersyarat , yang berpotensi digunakan di sini juga. Padahal, jenis komputasi mahal karena kompleksitas kombinasinya - kinerja menurun drastis untuk lebih dari 5-6 item. Saya mendaftar alternatif ini untuk kelengkapan ( 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"]
++-manual tuple dengan pelengkapan otomatis +tanpa --kinerja fungsi pembantu
Varian 4: Pembuat kode / API penyusun TS
ts-morph dipilih di sini, karena ini adalah alternatif pembungkus anak laki-laki yang lebih sederhana dari API kompilator TS asli . Tentu saja, Anda juga dapat menggunakan API kompilator secara langsung. Mari kita lihat kode generatornya:
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();
Setelah kami mengkompilasi dan menjalankan file ini dengan tsc && node dist/mybuildstep.js, file ./src/generated/IMyTable-keys.tsdengan konten berikut dihasilkan:
const keys = ["id","title","createdAt","isDeleted"] as const;
+solusi pembuatan otomatis yang dapat +diskalakan untuk beberapa properti, +tidak ada fungsi pembantu, +tuple, -langkah build tambahan, -perlu pemahaman dengan API kompiler