typescript - Bir sözlükte anahtar olarak jenerik bir nesneyi ve anahtar türünü nasıl kullanılır


Genel bir dizi içerisinde herhangi bir anahtarı kullanarak filtreleme yapabilen ve benzersiz öğeleri döndüren bir fonksiyon yapmak istiyorum. Ancak Typescript, T[Key]'yi bir dizi olarak kullanmama izin veriyor ama bir sözlüğün anahtarı olarak kullanmama izin vermiyor.

BU KOD ÇALIŞIYOR:

const unique = <T, Key extends keyof T>(array: T[], key: Key): T[] => {
  var uniqueArray: T[Key][] = []
  var distinct: T[] = []

  for (var i = 0; i < array.length; i++) {
    if (!uniqueArray.includes(array[i][key])) {
      distinct.push(array[i])
      uniqueArray.push(array[i][key])
    }
  }

  return distinct
}

export default unique

BU KOD ÇALIŞMIYOR:

const unique = <T, Key extends keyof T>(array: T[], key: Key): T[] => {
  var uniqueDict: {[T[Key]]: number} = {}
  var distinct: T[] = []

  for (var i = 0; i < array.length; i++) {
    if (!uniqueDict[array[i][key]]) {
      distinct.push(array[i])
      uniqueDict[array[i][key]] = 1
    }
  }

  return distinct
}

export default unique

İkinci kod örneği, var uniqueDict ... satırında A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1170) hatası vermektedir.

Eğer sözlüğü {[id: T[Key]]: number}={} olarak yazarsam, var uniqueDict ... satırında aynı zamanda An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.ts(1337) hatasını veriyor.

Benzersiz metodun sözlük versiyonunu nasıl çalıştıracağım konusunda herhangi bir yardım?

Cevap #1

İlk sorun, yapmaya çalıştığınız şey için bu geçerli bir sözdizimi değil:

const unique = <T, K extends keyof T>(array: T[], key: K): T[] => {  
  var uniqueDict: { [T[K]]: number } = {} // syntax error

Sözdizimi { [xxx]: number }, xxx bir hesaplanmış özellik adı olduğu ve bu nedenle bir anahtar benzeri değere karşılık gelmesi gerektiği bir türü belirtir. Orada hiç type kullanamazsınız, generik indekslenmiş erişim türü T[K] ise hiç kullanılamaz.

Bunu yazmanın doğru yolu, eşlenmiş bir tip kullanmaktır:

const unique = <T, K extends keyof T>(array: T[], key: K): T[] => {
  var uniqueDict: { [P in T[K]]: number } = {}; // error!    
  //                      ~~~~ <-- T[K] isn't known to be keylike

Bu, Record<K, V> yardımcı türünü kullanarak Record<T[K], number> ile eşdeğerdir.


Ama görüyorsunuz, hâlâ bir sorun var; derleyici T[K]'nin bir anahtar benzeri tür olduğunu bilmiyor. Sonuçta, T dizi'nin elemanlarının türü ve K key'in türü, ki bu da T'nin bir anahtarı olmalı. Yani T[K] o anahtardaki özellik... ama o özellik kendisi bir string mi? Muhtemelen hayır, rastgele bir T ve K için.

Bunu düzeltmek için, derleyicinin T[K]'nin string'e atanabilir olduğunu bilmesini sağlamak üzere T'yi kısıtlayabiliriz:

const unique = <T extends { [P in K]: string }, K extends keyof T>(array: T[], key: K): T[] => {
  var uniqueDict: { [P in T[K]]: number } = {}; // error!    
  //  ~~~~~~~~~~ <-- {} isn't known to be assignable to this type

T'yi {[P in K]: string} (bu, Record<K, string> ile eşdeğerdir) olarak sınırlayarak, [P in T[K]] adlı hatayı düzelttik.


Ama hâlâ bir sorunumuz var: Derleyici, türü T[K] olan her anahtara sahip bir özelliği olması gereken bir değişkene boş bir nesne atamanın geçerli olduğuna inanmıyor. Ve derleyicinin bir noktası var; o boş nesne atandığında böyle bir anahtara sahip değil.

Bu noktada ilerlemenin farklı yolları var. Sadece derleyiciye küçük bir yalan söyleyebilir ve boş nesnenin bu anahtarlara sahip olduğunu iddia edebilirsiniz:

var uniqueDict = {} as { [P in T[K]]: number }; // okay

Veya bunun yerine uniqueDict'e indekslenmiş erişim türü verebilirsiniz:

var uniqueDict: { [k: string]: number } = {}; // okay

Tip { [k: string]: number }, bazı rastgele string değerli anahtarlara sahip bir nesne anlamına gelir ve mevcut olan her anahtarda değer number türündedir. Bu, kodunuzun geri kalanının derleyici tarafından derlenmesi için yeterlidir. Varsayılan olarak, derleyicinin herhangi bir rastgele string anahtardan okumanıza izin verdiğini ve sanki orada kesinlikle bir özellik varmış gibi davrandığınızı unutmayın:

uniqueDict.randomKey.toFixed(2); // no compiler error, but probably runtime error

Kodunuzda bu tür varsayımlarda bulunmamaya dikkat ettiğiniz sürece, sorun yaşamazsınız. Aksi takdirde, daha güvenli olmak için --noUncheckedIndexedAccess derleyici seçeneğini açabilirsiniz (ancak bu daha rahatsız edici olur ve sadece bu kod parçasını değil, daha fazlasını etkiler) veya uniqueDict özelliğinin olası türlerine açıkça undefined ekleyebilirsiniz:

var uniqueDict: { [k: string]: number | undefined } = {}; // okay

Ama bu noktada bütün şey derleniyor, bu yüzden durabiliriz.

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