typescript - Bagaimana saya bisa menentukan tanda indeks kelas yang mengecualikan kunci indeks yang diwarisi yang diketahui?


Saya ingin membuat sebuah kelas yang mewarisi sebuah kelas, dan menentukan tanda indeks untuk semua properti kelas baru yang tidak ada di kelas induk.

Pertimbangkan situasi di mana Anda ingin menentukan beberapa format untuk kueri pencarian yang dapat mengambil array istilah pencarian pada bidang, dan yang mewarisi dari kelas kueri umum.

class Query {
  limit = 25;
  offset?: number;
  constructor() { /* do stuff */ }
}

class FieldSearchQuery extends Query {
  [key: Exclude<string, keyof Query>]: string[]; // not valid, but captures the gist
  constructor() { super(); /* do stuff */ }
}

Ada ide untuk alternatifnya? Perlu dicatat bahwa ada kelas dasar yang sudah ada, dan tujuan saya adalah mempersempit tipe dari subclass sehingga dapat menggunakan nilai kunci baru yang masuk yang tidak beririsan dengan kunci induk dan kita harus bisa memeriksa tipe bahwa nilai yang terkait dengan kunci yang tidak diketahui tersebut adalah dari tipe yang telah ditentukan (dalam contoh di atas, array string).

Saya terus melihat dokumentasi Tipe Lanjutan dan merasa seolah-olah saya melewatkan sesuatu, karena tampaknya semua alat untuk melakukan ini ada.

Edit: Saya baru saja menemukan kemungkinan perbaikan, di mana Anda setidaknya dapat mengecualikan semua tipe nilai yang tidak ada di induk:

class Query {
  limit = 25;
  offset?: number;
  constructor() { /* do stuff */ }
}

class FieldSearchQuery extends Query {
  [key: string]: Query[keyof Query]|string[]; // valid, but overly broad
  constructor() { super(); /* do stuff */ }
}

Jawaban #1

Apakah Anda yakin berada pada pola yang benar di sini? Karena jika Anda menentukan tanda indeks pada sebuah kelas, itu tidak hanya berlaku pada atribut kelas tetapi juga pada semua metode.

class B {
  [key: string]: string[]

  foo(): number { // Property 'foo' of type '() => number' is not assignable to string index type 'string[]'.
    return 1
  }
}

Salah satu cara untuk mencapai solusi Anda adalah dengan mengikuti contoh tipe irisan dalam dokumentasi TypeScript.

Solusi saya adalah dengan mendeklarasikan satu field pada kelas Anda, dan menentukan tipenya.

type QueryParams = { [key: string]: any } & {
  limit: number
  offset?: number
}

class Query<T extends QueryParams> {
  params: T
  constructor(params: Partial<T>) {
    this.params.limit = params.limit === undefined ? 25 : params.limit
  }
}

type FieldSearchQueryParams = { [key: string]: string[] } & {
  limit: number
  offset?: number
}

class FieldSearchQuery extends Query<FieldSearchQueryParams> {
  constructor(params: Partial<FieldSearchQueryParams>) {
    super(params)
    // do stuff
  }
}