Typescript - Maplenmiş Tür için Anahtarlar Üzerinde Döngü


Bir nesnenin anahtarları üzerinde döngü yapmak ve aynı anahtarlara sahip ama değerleri dönüştürülmüş yeni bir nesne döndürmek ve elde edilen türün giriş ile aynı anahtarlara sahip olmasını sağlamak için aşağıdaki yöntemden daha iyi bir yöntem var mı?

const env = {
  KEY_1: "VALUE_1",
  KEY_2: "ANOTHER_VALUE_2"
};

function mapValuesToLength<TEnv extends Record<string, string>>(
  env: TEnv
): Record<keyof TEnv, number> {
  const result: Partial<Record<keyof TEnv, number>> = {};

  for (const envKey in env) {
    result[envKey] = env[envKey].length;
  }

  return result as Record<keyof TEnv, number>;
}

// expecting result = {KEY_1: 7, KEY_2: 15}
const result = mapValuesToLength(env);

// expecting type resultType = { KEY_1: number; KEY_2: number; }
type resultType = typeof result

Cevap #1

Aşağıdaki yaklaşım hakkında ne düşünüyorsunuz (tek satır)?

const env = {
    KEY_1: "VALUE_1",
    KEY_2: "ANOTHER_VALUE_2"
};

type InputType = Record<string, string>;
type OutputType<T> = Record<keyof T, number>;

function mapValuesToLength<TEnv extends InputType>(env: TEnv): OutputType<TEnv> {
    return Object
            .entries(env)
            .reduce((acc, [key, value]) => ({ ...acc, [key]: value.length }), {} as OutputType<TEnv>);
}

const result = mapValuesToLength(env);
console.log(result);

Adımlar :

  1. Object.entries(): verilen bir nesnenin kendi sayılabilir string anahtarlı özellik [anahtar, değer] çiftlerinden oluşan bir dizi döndürür.
  2. Array.prototype.reduce(): dizinin her bir elemanı üzerinde bir azaltıcı (reducer) fonksiyonunu çalıştırır.

    1. ({ ...acc, [key]: value.length }): mevcut birikeni (sonuç nesnesi) yeni öğe (şu anki) key: value(length) ile döndürür.
    2. {} as OutputType<TEnv>: Toplayıcıyı, doğru çıktı tipine sahip boş bir nesne olarak başlatın.

Typescript Oyun Alanı

Cevap #2

Bir nesneyi başka bir nesneye dönüştürme konusunda sizin zorunlu (imperative) uygulamanızda bir sorun bulmuyorum. For döngüsünün ne yaptığını okumak ve anlamak basit.

Bu uygulama için array.reduce() fonksiyonel yaklaşımlarını da kullanabiliriz. Ama önce, nesneyi bir diziye dönüştürmemiz gerekiyor, ardından elde edilen diziyi istediğimiz sayı türündeki değerler nesnesine indirgememiz gerekiyor. Web sayfamın fazladan işlem yapmasını istemiyorum çünkü bu, performansı düşürecektir (çok olmasa da sadece for in'den daha yavaş olacaktır).

Kodunuzu türler açısından değiştirmek istiyorum.

const env = {
  KEY_1: "VALUE_1",
  KEY_2: "ANOTHER_VALUE_2",
};

// I don't like to put types inline which are too long.
type InputString<InputType> = Record<keyof InputType, string>;
type OutputNumber<InputType> = Record<keyof InputType, number>;

// no need to give a return type as typescript will correctly infer from the implementation.
function mapValuesToLength<TEnv extends InputString<TEnv>>(env: TEnv) {

  // type the result here using `as` 
  const result = {} as OutputNumber<TEnv>; 

  for (let envKey in env) {
    result[envKey] = env[envKey].length;
  }

  return result;
}

// expecting result = {KEY_1: 7, KEY_2: 15}
const result = mapValuesToLength(env);

// expecting type resultType = { KEY_1: number; KEY_2: number; }
type resultType = typeof result;