typescript - Bir özelliğin başka bir tipte var olup olmadığına bağlı olarak tipleri nasıl koşullu olarak seçebilirim?


Öncelikle, aşağıdaki sonucu istiyorum:

type Wrapper<ID extends string> = { id: ID };
type WrapperWithPayload<ID extends string, Payload> = { id: ID, payload: Payload };

enum IDs {
  FOO = "ID Foo",
  BAR = "ID Bar",
  BAZ = "ID Baz"
}

interface AssociatedTypes {
  [IDs.FOO]: number;
  [IDs.BAR]: number[];
}

type Result = MagicMapper<IDs, AssociatedTypes>
/*
 * Result should have this type:
 * {
 *   [IDs.FOO]: WrapperWithPayload<IDs.FOO, number>;
 *   [IDs.BAR]: WrapperWithPayload<IDs.BAR, number[]>;
 *   [IDs.BAZ]: Wrapper<IDs.BAZ>;
 * }
 */

Başka bir deyişle, bazı dizeleri ve bu dizelerin alt kümesini başka bir türe eşleyen bir eşlemeyi sağlamak istiyorum. Sonra, dizeleri anahtar olarak kullanarak yeni bir tür oluşturmak istiyorum. Dizelerin/anahtarların her biri için, bir eşleme varsa tür A'yı, yoksa tür B'yi kullanmak istiyorum.

Şu anda yaklaşımım şöyle görünüyor:

type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
    [key in T]: Mapping[key] extends never ? Wrapper<key> : WrapperWithPayload<key, Mapping[key]>;
};

Ve o neredeyse golü vuruyor:

type Result = {
  "ID Foo": WrapperWithPayload<IDs.FOO, number>;
  "ID Bar": WrapperWithPayload<IDs.BAR, number[]>;
  "ID Baz": Wrapper<IDs.BAZ> | WrapperWithPayload<IDs.BAZ, any>;
}

Baz anahtarı üzerindeki birleşim yanlış. Sanırım hata extends never koşulunda yatıyor, ancak bunu örneğin undefined ile değiştirmek işleri sadece daha kötü yapıyor:

type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
    [key in T]: Mapping[key] extends undefined ? Wrapper<key> : WrapperWithPayload<key, Mapping[key]>;
};

type Result = {
  "ID Foo": Wrapper<IDs.FOO>;
  "ID Bar": Wrapper<IDs.BAR>;
  "ID Baz": Wrapper<IDs.BAZ>;
}

İşlerin benim ihtiyacım olan şekilde ilerlemesini sağlamanın bir yolu var mı?

Cevap #1

Mapping içindeki anahtarın varlığını test etme şeklini Mapping extends { [P in key]: infer U } olarak değiştirebilirsiniz ve her şey beklendiği gibi çalışacaktır:

type Wrapper<ID extends string> = { id: ID };
type WrapperWithPayload<ID extends string, Payload> = { id: ID, payload: Payload };

enum IDs {
    FOO = "ID Foo",
    BAR = "ID Bar",
    BAZ = "ID Baz"
}

interface AssociatedTypes {
    [IDs.FOO]: number;
    [IDs.BAR]: number[];
}

type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
    [key in T]: Mapping extends { [P in key]: infer U } ? WrapperWithPayload<key, U>: Wrapper<key> ;
};
type Result = MagicMapper<IDs, AssociatedTypes> 
// same as :
type Result = {
    "ID Foo": WrapperWithPayload<IDs.FOO, number>;
    "ID Bar": WrapperWithPayload<IDs.BAR, number[]>;
    "ID Baz": Wrapper<IDs.BAZ>;
}

Oyun alanı linki

İlgili makaleler