Saya memiliki banyak tabel di Lovefield dan Antarmuka masing-masing untuk kolom apa yang mereka miliki.
Contoh:
export interface IMyTable {
id: number;
title: string;
createdAt: Date;
isDeleted: boolean;
}
Saya ingin memiliki nama properti antarmuka ini dalam array seperti ini:
const IMyTable = ["id", "title", "createdAt", "isDeleted"];
Saya tidak dapat membuat objek / array berdasarkan antarmuka IMyTable
secara langsung yang seharusnya melakukan trik karena saya akan mendapatkan nama antarmuka tabel secara dinamis. Oleh karena itu saya perlu mengulang properti ini di antarmuka dan mendapatkan array darinya.
Bagaimana cara saya mencapai hasil ini?
sumber
ts_transformer_keys_1.keys is not a function
ketika saya mengikuti langkah-langkah yang tepat dalam dokumentasi. apakah ada solusi untuk ini?ts_transformer_keys_1.keys is not a function
Hal berikut mengharuskan Anda untuk membuat daftar kunci Anda sendiri, tetapi setidaknya TypeScript akan diberlakukan
IUserProfile
danIUserProfileKeys
memiliki kunci yang sama persis (Required<T>
ditambahkan di TypeScript 2.8 ):export interface IUserProfile { id: string; name: string; }; type KeysEnum<T> = { [P in keyof Required<T>]: true }; const IUserProfileKeys: KeysEnum<IUserProfile> = { id: true, name: true, };
sumber
IUserProfile
dan akan mudah untuk mengekstraknya dari constIUserProfileKeys
. Inilah yang saya cari-cari. Tidak perlu mengonversi semua antarmuka saya ke kelas sekarang.Saya memiliki masalah serupa yaitu saya memiliki daftar besar properti yang saya inginkan untuk memiliki antarmuka dan objek darinya.
CATATAN: Saya tidak ingin menulis (mengetik dengan keyboard) properti dua kali! KERING saja.
Satu hal yang perlu diperhatikan di sini adalah, antarmuka adalah tipe yang diberlakukan pada waktu kompilasi, sedangkan objek sebagian besar merupakan waktu proses. ( Sumber )
Seperti yang disebutkan @derek di jawaban lain , penyebut umum antarmuka dan objek dapat berupa kelas yang melayani tipe dan nilai .
Jadi, TL; DR, potongan kode berikut harus memenuhi kebutuhan:
class MyTableClass { // list the propeties here, ONLY WRITTEN ONCE id = ""; title = ""; isDeleted = false; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // This is the pure interface version, to be used/exported interface IMyTable extends MyTableClass { }; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Props type as an array, to be exported type MyTablePropsArray = Array<keyof IMyTable>; // Props array itself! const propsArray: MyTablePropsArray = Object.keys(new MyTableClass()) as MyTablePropsArray; console.log(propsArray); // prints out ["id", "title", "isDeleted"] // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Example of creating a pure instance as an object const tableInstance: MyTableClass = { // works properly! id: "3", title: "hi", isDeleted: false, };
( Ini adalah kode di atas di Typecript Playground untuk bermain lebih banyak)
PS. Jika Anda tidak ingin menetapkan nilai awal ke properti di kelas, dan tetap menggunakan tipenya, Anda dapat melakukan trik konstruktor:
class MyTableClass { // list the propeties here, ONLY WRITTEN ONCE constructor( readonly id?: string, readonly title?: string, readonly isDeleted?: boolean, ) {} } console.log(Object.keys(new MyTableClass())); // prints out ["id", "title", "isDeleted"]
Trik Konstruktor di TypeScript Playground .
sumber
propsArray
hanya dapat diakses ketika Anda menginisialisasi kunci.propsArray
tersedia sebelumtableInstance
jika itu yang Anda maksud, jadi jelas sebelum inisialisasi contoh. Namun, jika Anda mengacu pada nilai palsu yang ditetapkanMyTableClass
, nilai tersebut hanya ada untuk menyiratkan "jenis" properti. Jika Anda tidak menginginkannya, Anda dapat menggunakan trik konstruktor dalam contoh PS.MyTableClass
dengan yang terakhir dan berharap untuk menerima kunci dalampropsArray
vars dan tipe yang tidak diinisialisasi dihapuskan pada waktu proses. Anda selalu harus memberikan mereka beberapa jenis nilai default. Saya telah menemukan bahwa menginisialisasi mereka denganundefined
adalah pendekatan terbaik.Mungkin sudah terlambat, tapi di typecript versi 2.1 anda bisa menggunakan
key of
seperti ini:interface Person { name: string; age: number; location: string; } type K1 = keyof Person; // "name" | "age" | "location" type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ... type K3 = keyof { [x: string]: Person }; // string
Doc: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types
sumber
Ini seharusnya berhasil
var IMyTable: Array<keyof IMyTable> = ["id", "title", "createdAt", "isDeleted"];
atau
var IMyTable: (keyof IMyTable)[] = ["id", "title", "createdAt", "isDeleted"];
sumber
var IMyTable: Array<keyof IMyTable> = ["id", "createdAt", "id"];
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
// Record type ensures, we have no double or missing keys, values can be neglected 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 }) // const keys: ("id" | "title" | "createdAt" | "isDeleted")[]
+
+-
manual termudah dengan pelengkapan otomatis-
larik , tanpa tupelTempat bermain
Jika Anda tidak suka membuat record, lihat alternatif with
Set
dan 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 kompleksTempat bermain
Penjelasan
createKeys
melakukan 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.CheckMissing
cek, jikaT
melewatkan kunci dariU
: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}> //["p1"] & "Error: missing keys" type T2 = CheckMissing<["p1", "p2"], { p1: any, p2: any }> // ["p1", "p2"]
Catatan:
T & "Error: missing keys"
hanya untuk kesalahan IDE yang bagus. Anda juga bisa menulisnever
.CheckDuplicates
memeriksa 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]> // [1, 2, 3] type T4 = CheckDuplicate<[1, 2, 1]> // [[1, "Error: duplicate"], 2, [1, "Error: duplicate"]]
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] // TS 4.0 variadic tuples 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 pembantuVarian 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:
// ./src/mybuildstep.ts import {Project, VariableDeclarationKind, InterfaceDeclaration } from "ts-morph"; const project = new Project(); // source file with IMyTable interface const sourceFile = project.addSourceFileAtPath("./src/IMyTable.ts"); // target file to write the keys string array to const destFile = project.createSourceFile("./src/generated/IMyTable-keys.ts", "", { overwrite: true // overwrite if exists }); 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(); // flush all changes and write to disk
Setelah kami mengkompilasi dan menjalankan file ini dengan
tsc && node dist/mybuildstep.js
, file./src/generated/IMyTable-keys.ts
dengan konten berikut dihasilkan:// ./src/generated/IMyTable-keys.ts 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 kompilersumber
Alih-alih mendefinisikan
IMyTable
sebagai antarmuka, coba definisikan sebagai kelas. Dalam skrip ketikan Anda dapat menggunakan kelas seperti antarmuka.Jadi untuk contoh Anda, tentukan / buat kelas Anda seperti ini:
export class IMyTable { constructor( public id = '', public title = '', public createdAt: Date = null, public isDeleted = false ) }
Gunakan sebagai antarmuka:
export class SomeTable implements IMyTable { ... }
Dapatkan kunci:
const keys = Object.keys(new IMyTable());
sumber
Anda perlu membuat kelas yang mengimplementasikan antarmuka Anda, membuat instance-nya, lalu menggunakan
Object.keys(yourObject)
untuk mendapatkan properti.export class YourClass implements IMyTable { ... }
kemudian
let yourObject:YourClass = new YourClass(); Object.keys(yourObject).forEach((...) => { ... });
sumber
Cannot extend an interface […]. Did you mean 'implements'?
Namun, menggunakanimplements
sebaliknya memerlukan penyalinan antarmuka secara manual, yang sebenarnya tidak saya inginkan.implements
dan saya telah memperbarui jawaban saya. Seperti yang dikatakan @basarat, antarmuka tidak ada pada waktu proses jadi satu-satunya cara adalah mengimplementasikannya sebagai kelas.@types/react
). Saya menyalinnya secara manual, tapi itu hampir tidak tahan masa depan 😪 Saya mencoba mengikat metode non-siklus hidup secara dinamis (yang sudah terikat), tetapi metode tersebut tidak dideklarasikan di React.Component (kelas).Tidak bisa. Antarmuka tidak ada saat runtime.
solusi
Buat variabel dari tipe dan gunakan
Object.keys
di atasnya 🌹sumber
var abc: IMyTable = {}; Object.keys(abc).forEach((key) => {console.log(key)});
Ini yang sulit! Terima kasih, semuanya, atas bantuan Anda.
Kebutuhan saya adalah mendapatkan kunci antarmuka sebagai array string untuk menyederhanakan skrip mocha / chai. Tidak peduli tentang penggunaan di aplikasi (belum), jadi tidak perlu file ts untuk dibuat. Terima kasih kepada ford04 atas bantuannya, solusinya di atas sangat membantu dan berfungsi dengan sempurna, TANPA peretasan kompiler. Berikut kode yang dimodifikasi:
Opsi 2: Pembuat kode berdasarkan TS compiler API (ts-morph)
Modul Node
keys.ts
CATATAN : ini mengasumsikan semua file ts terletak di root ./src dan tidak ada subfolder, sesuaikan
import { Project, VariableDeclarationKind, InterfaceDeclaration, } from "ts-morph"; // initName is name of the interface file below the root, ./src is considered the root const Keys = (intName: string): string[] => { const project = new Project(); const sourceFile = project.addSourceFileAtPath(`./src/${intName}.ts`); const node = sourceFile.getInterface(intName)!; const allKeys = node.getProperties().map((p) => p.getName()); return allKeys; }; export default Keys;
pemakaian
import keys from "./keys"; const myKeys = keys("MyInterface") //ts file name without extension console.log(myKeys)
sumber
Anda tidak bisa melakukannya. Antarmuka tidak ada saat runtime (seperti yang dikatakan @basarat ).
Sekarang, saya sedang mengerjakan yang berikut:
const IMyTable_id = 'id'; const IMyTable_title = 'title'; const IMyTable_createdAt = 'createdAt'; const IMyTable_isDeleted = 'isDeleted'; export const IMyTable_keys = [ IMyTable_id, IMyTable_title, IMyTable_createdAt, IMyTable_isDeleted, ]; export interface IMyTable { [IMyTable_id]: number; [IMyTable_title]: string; [IMyTable_createdAt]: Date; [IMyTable_isDeleted]: boolean; }
sumber
// declarations.d.ts export interface IMyTable { id: number; title: string; createdAt: Date; isDeleted: boolean } declare var Tes: IMyTable; // call in annother page console.log(Tes.id);
sumber
console.log(Tes.id)
yang tentu saja akan menjadi kesalahan 'Uncaught ReferenceError: Tes tidak ditentukan'