Daktilo yazısında Kayıt türü nedir?


185

Record<K, T>Daktilo Yazısı ne anlama geliyor?

Typcript 2.1 Record, bir örnekte açıklayan türü tanıttı :

// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>

daktiloya bakınız 2.1

Ve Gelişmiş Çeşitleri sayfa bahseder Recordyanında başlığı Haritalanmış Türleri altında Readonly, Partialve Pickonun tanımı gibi görünen de,:

type Record<K extends string, T> = {
    [P in K]: T;
}

Salt okunur, Kısmi ve Seçim homomorfikken, Kayıt değildir. Record'un homomorfik olmadığına dair bir ipucu, özellikleri kopyalamak için bir girdi türü almamasıdır:

type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

Ve bu kadar. Yukarıdaki alıntıların yanı sıra, typescriptlang.orgRecord üzerinde başka bir söz yoktur .

Sorular

  1. Birisi neyin basit bir tanımını verebilir Record?

  2. Record<K,T>sadece "Bu nesne üzerindeki tüm özelliklerini tip olacak demenin bir yolu T"? Muhtemelen tüm özellikler değil , çünkü Kbir amacı var ...

  3. KJenerik, nesnede olmayan ek anahtarları yasaklıyor mu Kyoksa bunlara izin veriyor mu ve sadece özelliklerinin dönüştürülmediğini gösteriyor Tmu?

  4. Verilen örnekle:

     type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

Bu tam olarak aynı mı ?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}

6
4. yanıtı hemen hemen "evet" dir, bu yüzden muhtemelen diğer sorularınızı cevaplamalıdır.
jcalz

Yanıtlar:


213
  1. Birisi neyin basit bir tanımını verebilir Record?

A Record<K, T>, özellik anahtarları olan Kve özellik değerleri olan bir nesne türüdür T. Yani, keyof Record<K, T>eşittir Kve Record<K, T>[K](temelde) eşittir T.

  1. Record<K,T>sadece "Bu nesne üzerindeki tüm özelliklerini tip olacak demenin bir yolu T"? Muhtemelen tüm nesneler değil, çünkü Kbir amacı var ...

Belirttiğiniz gibi K, özellik anahtarlarını belirli değerlerle sınırlamak ... bir amacı vardır. Tüm olası dize değerli anahtarları kabul etmek istiyorsanız, böyle bir şey yapabilirsiniz Record<string, T>, ancak bunu yapmanın deyimsel yolu gibi bir dizin imzası kullanmaktır { [k: string]: T }.

  1. KJenerik, nesnede olmayan ek anahtarları yasaklıyor mu Kyoksa bunlara izin veriyor mu ve sadece özelliklerinin dönüştürülmediğini gösteriyor Tmu?

Ek anahtarları tam olarak "yasaklamaz": sonuçta, bir değerin genellikle türünde açıkça belirtilmeyen özelliklere sahip olmasına izin verilir ... ancak bu tür özelliklerin var olduğunu kabul etmez:

declare const x: Record<"a", string>;
x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'

ve onlara bazen reddedilen aşırı özellikler olarak muamele ederdi :

declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties

ve bazen kabul edilebilir:

const y = {a: "hey", b: "you"};
acceptR(y); // okay
  1. Verilen örnekle:

    type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

    Bu tam olarak aynı mı ?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}

Evet!

Umarım yardımcı olur. İyi şanslar!


1
Çok şey öğrendim ve bir soru neden "bunu yapmanın deyimsel yolu bir indeks imzası kullanmak" bir Kayıt değil? Bu "deyimsel yol" ile ilgili herhangi bir bilgi bulamıyorum.
legend80s

2
İsterseniz Record<string, V>demek için kullanabilirsiniz {[x: string]: V}; Muhtemelen bunu kendim bile yaptım. Dizin imzası sürümü daha doğrudan: aynı türdür, ancak birincisi, bir dizin imzası olarak değerlendirilen eşlenmiş türün bir tür diğer adıdır, ikincisi ise doğrudan dizin imzasıdır. Diğer her şey eşit, ikincisini tavsiye ederim. Benzer şekilde Record<"a", string>, {a: string}içeriğe dayalı başka zorlayıcı bir neden olmadıkça yerine kullanmazdım .
jcalz

1
“Diğer her şey eşitken, ikincisini tavsiye ederim. ” Neden? Benim ön-Typecript kabul ediyorum, ama eski, örneğin C # tarafından gelen insanlar için daha fazla, um, kendi yorum yapmak biliyorum ve JavaScript-Türleri için kötü değil biliyorum. Sadece bu yapılar için transpilasyon adımını atlamakla ilgileniyor musunuz?
ruffin

1
Sadece benim düşüncem: davranışının Record<string, V>sadece dizin imzalarının TypeScript'te nasıl çalıştığını zaten biliyorsanız mantıklı. Örneğin, verilen x: Record<string, string>, x.foogörünüşe göre bir stringderleme zamanında olacak, ama gerçekte olması muhtemeldir string | undefined. Bu, nasıl --strictNullChecksçalıştığı konusunda bir boşluktur (bkz. # 13778 ). Daha doğrusu yeni gelenler ile anlaşma olurdu {[x: string]: V}, zinciri takip etmelerini bekliyoruz doğrudan yerine Record<string, V>aracılığıyla {[P in string]: V}endeks imza davranışına.
jcalz

Mantıksal olarak bir türün içerdiği tüm değerlerin bir kümesi olarak tanımlanabileceğini belirtmek istedim. Bu yorum göz önüne alındığında, Record <string, V> 'un tüm olası değerleri ortaya koymak yerine kodu basitleştirmek için bir soyutlama olduğunu düşünüyorum. Yardımcı program tipi belgelerindeki örneğe benzer: Kayıt T <V, V> burada T tipi = 'a' | 'b' | 'c'. Asla <'a', string> kaydı yapmamak iyi bir sayaç örneği değildir, çünkü aynı kalıbı takip etmez. Ayrıca, diğer örneklerde olduğu gibi, kodun soyutlama yoluyla yeniden kullanılmasını veya basitleştirilmesini de içermez.
Scott Leonard

69

Kayıt, Bir Birlik'ten yeni bir tür oluşturmanıza olanak tanır. Birlik'teki değerler yeni türün nitelikleri olarak kullanılır.

Örneğin, şöyle bir Birliğim olduğunu varsayalım:

type CatNames = "miffy" | "boris" | "mordred";

Şimdi tüm kediler hakkında bilgi içeren bir nesne oluşturmak istiyorum, CatName Union'daki değerleri anahtar olarak kullanarak yeni bir tür oluşturabilirim.

type CatList = Record<CatNames, {age: number}>

Bu CatList karşılamak istiyorsanız, böyle bir nesne oluşturmanız gerekir:

const cats:CatList = {
  miffy: { age:99 },
  boris: { age:16 },
  mordred: { age:600 }
}

Çok güçlü tip güvenliği elde edersiniz:

  • Bir kediyi unutursam, bir hata alıyorum.
  • İzin verilmeyen bir kedi eklersem bir hata alırım.
  • Daha sonra CatNames'i değiştirirsem bir hata alıyorum. Bu özellikle yararlıdır çünkü CatNames muhtemelen başka bir dosyadan içe aktarılır ve birçok yerde kullanılır.

Gerçek dünya Tepkisi örneği.

Bunu bir Durum bileşeni oluşturmak için yakın zamanda kullandım. Bileşen bir durum desteği alır ve ardından bir simge oluşturur. Açıklayıcı amaçlı kodu oldukça basitleştirdim.

Böyle bir birliğim vardı:

type Statuses = "failed" | "complete";

Ben böyle bir nesne oluşturmak için kullandım:

const icons: Record<
  Statuses,
  { iconType: IconTypes; iconColor: IconColors }
> = {
  failed: {
    iconType: "warning",
    iconColor: "red"
  },
  complete: {
    iconType: "check",
    iconColor: "green"
  };

Sonra nesne gibi bir öğeyi sahne içine yıkarak render, şöyle:

const Status = ({status}) => <Icon {...icons[status]} />

Durumlar birleşimi daha sonra genişletilir veya değiştirilirse, Durum bileşenimin derlenemediğini ve hemen düzeltebileceğim bir hata alacağımı biliyorum. Bu, uygulamaya ek hata durumları eklememe izin veriyor.

Gerçek uygulamanın birden fazla yerde başvurulan düzinelerce hata durumu olduğunu unutmayın, bu nedenle bu tür güvenlik son derece kullanışlıdır.


Çoğu zaman type Statusessizin tarafınızdan tanımlanmayan yazılar yazıyor mu? Aksi takdirde bir enum daha iyi bir uyum ile bir arayüz gibi bir şey görebilirsiniz değil mi?
Victorio Berra

Merhaba @victorio, bir numaralandırma sorunu nasıl çözeceğinden emin değilim, bir anahtarı kaçırırsanız numaralandırmada hata almazsınız. Bu sadece anahtarlar ve değerler arasında bir eşleme.
superluminary

1
Ne demek istediğini şimdi anlıyorum. C # 'dan gelince, bunu yapmanın akıllı yolları yok. En yakın şey bir sözlük olurdu Dictionary<enum, additional_metadata>. Kayıt türü, bu enum + meta veri modelini temsil etmenin harika bir yoludur.
Victorio Berra
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.