Typescript ile diziyi, anahtarlar üzerinde intellisense ile nesneye yeniden haritalama


Şu anda Typescript Haritalı Türlerini anlamakta biraz sıkıntı yaşıyorum, burada "Anahtar yeniden eşleme"nin mümkün olduğunu belirtiyorlar, ancak aslında bir dizi ile bunu denediğimde, sadece Type '"key"' cannot be used to index type 'A[E]' hatası alıyorum.

Not: Bu bir tipler sorusudur, çalışma zamanı sorusu değildir.

Örnek Kod:

interface ListEntry {
  key: string;
  prop: string;
}

type MapListEntryArrayToRecord<A extends ListEntry[]> = {
  // Error "Type '"key"' cannot be used to index type 'A[E]'"
  [E in keyof A as A[E]['key']]: A[E]['prop'];
};

const input: ListEntry[] = [
  { key: 'key1', prop: 'Prop1' },
  { key: 'key2', prop: 'Prop2' },
];

function someFunction<List extends ListEntry[]>(list: List): MapListEntryArrayToRecord<List> {
  // some implementation
  const ret: Record<string, ListEntry['prop']> = {};
  for (const { key, prop } of list) {
    ret[key] = prop;
  }

  return ret as MapListEntryArrayToRecord<List>;
}

const mapped = someFunction(input);

// expecting intellisense for all available keys
mapped;

// example of what i would expect
const expectedOutput = {
  key1: 'Prop1',
  key2: 'Prop2',
};

// expecting intellisense for all available keys
expectedOutput;

Not: Bir cevap aramaya çalıştım ama bunu nasıl yapacağımı gösteren herhangi bir TypeScript örneği bulamadım.

Cevap #1

Burada birkaç problem var.

Önce input nesnesi. Eğer açık bir tip kullanırsanız, ona atadığınız nesnenin belirli değerleriyle ilgili tüm bilgiler kaybolur. Bu yüzden tipi kaldırmamız ve as const eklememiz gerekir.

const input = [
  { key: 'key1', prop: 'Prop1' },
  { key: 'key2', prop: 'Prop2' },
] as const;

as const kullanmak, diziyi readonly hâline getirecektir. Hem normal hem de readonly dizileri kabul etmek için genel türleri değiştirmemiz gerekecek.

type MapListEntryArrayToRecord<A extends readonly ListEntry[]> = {
  /* ... */
};

function someFunction<List extends readonly ListEntry[]>(list: List): MapListEntryArrayToRecord<List> {
  /* ... */
}

Şimdi asıl sorunuza gelelim. TypeScript, A[keyof A] ifadesinin bir key özelliğine sahip olduğunu bilmekte sorun yaşar. Bu, ana hatlarıyla, generik türlerin anahtarları ile indekslenen generik türler ve benzeri ifadelerin derleyici tarafından genellikle tamamen değerlendirilmemesi gerçeğine dayanır.

Yani, bu özelliğin var olduğunu TypeScript'e açıkça kontrol ederek hatırlatmamız gerekiyor.

type MapListEntryArrayToRecord<A extends readonly ListEntry[]> = {
  [E in keyof A as A[E] extends { key: infer K extends string } ? K : never]: 
    A[E]['prop'];
};

Oyun alanı