typescript - Arayüzdeki özellik tiplerinin birbirine bağlı olmasını nasıl sağlarım?


dataItem'in her zaman field özelliğinin değeri ile aynı türde olmasını sağlayacak dinamik bir yol bulamıyor gibiyim. Aşağıdaki örnekte dataItem her zaman string | Date | number olur, field özelliğinin ne olduğuna bağlı olarak belirli türde olmaz.

interface TableColumn<D> {
  field: keyof D
  renderCell: (dataItem: D[keyof D]) => ReactNode
}

interface Data {
  foo: string
  bar: Date
  fiz: number
}

const data: Data = {
  foo: "my foo",
  bar: new Date(),
  fiz: 32
}

const tableColumn: TableColumn<Data> = {
  field: "foo",
  renderCell: (dataItem) => dataItem // here dataItem should be string only
}

const tableColumn1: TableColumn<Data> = {
  field: "bar",
  renderCell: (dataItem) => dataItem // here dataItem should be Date only
}

const tableColumn2: TableColumn<Data> = {
  field: "fiz",
  renderCell: (dataItem) => dataItem // here dataItem should be number only
}

Cevap #1

Bir arayüz bu şekilde çalışmaz; muhtemelen bunu daha genel bir hale getirebilirsiniz, örneğin TableColumn<D, K> gibi, burada K, keyof D içindeki belirli bir anahtar türüdür, ancak o zaman K'yı her yerde belirtmeniz gerekecek.

Bunun yerine, TableColumn<D> öğesini, microsoft/TypeScript#47109'da adlandırıldığı gibi bir dağıtıcı nesne türü yapardım; bu tür, keyof T içindeki her K ile ilişkili bireysel türlerin birleşimine değerlendirilir. Dağıtıcı nesne türü, keyof D içindeki her özellik değerini, ilgili özellik anahtarına karşılık gelen istenen türü tutan bir tipe dönüştüren ve ardından hemen keyof D ile indekslenen bir eşlenen türdür. Şöyle:

type TableColumn<D> = { [K in keyof D]-?:
  {
    field: K,
    renderCell: (dataItem: D[K]) => ReactNode
  }
}[keyof D]

Burada, { [K in keyof D]-?: { field: K, renderCell: (dataItem: D[K]) => ReactNode }} eşlenen türünü üretiyoruz, bu Data için şuna benzer bir şey olur

{
  foo: { field: "foo"; renderCell: (dataItem: string) => ReactNode }
  bar: { field: "bar"; renderCell: (dataItem: Date) => ReactNode }
  fiz: { field: "fiz"; renderCell: (dataItem: number) => ReactNode }
}

Dikkat edin, -? eşleme değiştiricisi, D içindeki herhangi bir isteğe bağlı özelliğin eşlenen türde zorunlu hale gelmesini sağlar, çünkü burada herhangi bir undefined değerin dolaşmasını istemeyiz.

Sonra buna {...}[keyof D] üzerinden indeksleme yapıyoruz, bu da o eşlenmiş türü değer türlerinin bir birleşimi haline getiriyor ve sonunda şunu üretiyor:

type TCD = TableColumn<Data>
/* type TCD = {
    field: "foo";
    renderCell: (dataItem: string) => ReactNode;
} | {
    field: "bar";
    renderCell: (dataItem: Date) => ReactNode;
} | {
    field: "fiz";
    renderCell: (dataItem: number) => ReactNode;
} */

İşte istediğiniz birleşim türü; aslında, field özelliğinin ayırt edici özellik olduğu bir ayrılmış birleşimdir. Bu, derleyicinin field türünden renderCell türünü çıkarmasını sağlar ve böylece renderCell’in açıklanmamış geri arama parametresinde istediğiniz bağlamsal türlemeyi verir:

const tableColumn: TableColumn<Data> = {
  field: "foo",
  renderCell: (dataItem) => dataItem
  // (parameter) dataItem: string
}

const tableColumn1: TableColumn<Data> = {
  field: "bar",
  renderCell: (dataItem) => dataItem
  // (parameter) dataItem: Date
}

const tableColumn2: TableColumn<Data> = {
  field: "fiz",
  renderCell: (dataItem) => dataItem
  // (parameter) dataItem: number
}

Koda yönelik oyun alanı bağlantısı