Apa jenis Catatan dalam naskah?


183

Apa yang dimaksud Record<K, T>dalam naskah?

Jenis naskah 2.1 memperkenalkan Recordjenis tersebut, menjelaskannya dalam contoh:

// 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>

lihat naskah 2.1

Dan Jenis Lanjutan halaman menyebutkan Recorddi bawah Jenis dipetakan menuju samping Readonly, Partialdan Pick, dalam apa yang tampaknya menjadi definisi:

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

Readonly, Partial dan Pick bersifat homomorfik sedangkan Record tidak. Satu petunjuk bahwa Record bukan homomorphic adalah bahwa ia tidak mengambil tipe input untuk menyalin properti dari:

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

Dan itu saja. Selain kutipan di atas, tidak ada lagi disebutkan Recorddi typescriptlang.org .

Pertanyaan

  1. Adakah yang bisa memberikan definisi sederhana tentang apa Recorditu?

  2. Apakah Record<K,T>hanya cara mengatakan "semua properti pada objek ini akan memiliki tipe T"? Mungkin tidak semua properti, karena Kmemiliki beberapa tujuan ...

  3. Apakah Kgenerik melarang kunci tambahan pada objek yang tidak K, atau apakah itu memperbolehkannya dan hanya menunjukkan bahwa propertinya tidak ditransformasikan T?

  4. Dengan contoh yang diberikan:

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

Apakah persis sama dengan ini ?:

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

6
Jawaban untuk 4. cukup banyak "ya", sehingga mungkin harus menjawab pertanyaan Anda yang lain.
jcalz

Jawaban:


211
  1. Adakah yang bisa memberikan definisi sederhana tentang apa Recorditu?

A Record<K, T>adalah tipe objek yang memiliki kunci properti Kdan yang memiliki nilai properti T. Artinya, keyof Record<K, T>sama dengan K, dan Record<K, T>[K](pada dasarnya) setara dengan T.

  1. Apakah Record<K,T>hanya cara mengatakan "semua properti pada objek ini akan memiliki tipe T"? Mungkin tidak semua benda, karena Kmemiliki beberapa tujuan ...

Seperti yang Anda perhatikan, Kmemiliki tujuan ... untuk membatasi kunci properti hingga nilai tertentu. Jika Anda ingin menerima semua kunci bernilai string yang mungkin, Anda bisa melakukan sesuatu seperti Record<string, T>, tetapi cara idiomatis melakukan itu adalah dengan menggunakan tanda tangan indeks seperti { [k: string]: T }.

  1. Apakah Kgenerik melarang kunci tambahan pada objek yang tidak K, atau apakah itu memperbolehkannya dan hanya menunjukkan bahwa propertinya tidak ditransformasikan T?

Itu tidak persis "melarang" kunci tambahan: setelah semua, nilai umumnya diizinkan untuk memiliki properti yang tidak disebutkan secara eksplisit dalam jenisnya ... tetapi itu tidak akan mengenali bahwa properti tersebut ada:

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

dan itu akan memperlakukan mereka sebagai properti berlebih yang kadang-kadang ditolak:

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

dan terkadang diterima:

const y = {a: "hey", b: "you"};
acceptR(y); // okay
  1. Dengan contoh yang diberikan:

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

    Apakah persis sama dengan ini ?:

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

Iya!

Semoga itu bisa membantu. Semoga berhasil!


1
Sangat banyak dipelajari dan pertanyaan mengapa "cara idiomatis melakukan itu adalah dengan menggunakan tanda tangan indeks" bukan yang Record? Saya tidak menemukan informasi terkait tentang "cara idiomatik" ini.
legend80s

2
Anda bisa menggunakan Record<string, V>artinya {[x: string]: V}jika Anda mau; Saya bahkan mungkin sudah melakukan ini sendiri. Versi tanda tangan indeks lebih langsung: mereka adalah tipe yang sama, tetapi yang pertama adalah jenis alias dari tipe yang dipetakan yang mengevaluasi ke tanda tangan indeks, sedangkan yang terakhir hanya tanda tangan indeks secara langsung. Semuanya sama, saya akan merekomendasikan yang terakhir. Demikian pula saya tidak akan menggunakannya Record<"a", string>sebagai pengganti {a: string}kecuali ada alasan kontekstual menarik lainnya melakukannya.
jcalz

1
" Semuanya sama, saya akan merekomendasikan yang terakhir. " Mengapa begitu? Prapengisian naskah saya setuju, tapi saya tahu yang pertama akan lebih, um, berkomentar sendiri untuk orang-orang yang datang dari sisi C #, misalnya, dan tidak lebih buruk untuk JavaScript-ke-pengetik. Apakah Anda hanya tertarik melewatkan langkah transpilasi untuk konstruksi tersebut?
ruffin

1
Hanya pendapat saya: perilaku Record<string, V>hanya masuk akal jika Anda sudah tahu cara kerja tanda tangan indeks di TypeScript. Misalnya, diberikan x: Record<string, string>, x.footampaknya akan berada stringpada waktu kompilasi, tetapi dalam kenyataannya mungkin string | undefined. Ini adalah celah dalam cara --strictNullCheckskerjanya (lihat # 13778 ). Saya lebih suka membuat kesepakatan dengan pendatang baru {[x: string]: V}secara langsung daripada mengharapkan mereka untuk mengikuti rantai dari Record<string, V>sampai {[P in string]: V}perilaku indeks.
jcalz

Saya ingin menunjukkan bahwa secara logis suatu tipe dapat didefinisikan sebagai satu set semua nilai yang terkandung dalam tipe tersebut. diberikan interpretasi ini saya pikir Rekam <string, V> masuk akal sebagai abstraksi untuk menyederhanakan kode alih-alih meletakkan semua nilai yang mungkin. Ini mirip dengan contoh dalam dokumentasi tipe utilitas: Catat <T, V> di mana tipe T = 'a' | 'b' | 'c'. Jangan pernah melakukan Rekam <'a', string> bukan contoh penghitung yang baik, karena tidak mengikuti pola yang sama. Itu juga tidak menambah untuk menggunakan kembali atau menyederhanakan kode melalui abstraksi seperti contoh lainnya.
Scott Leonard

69

Catatan memungkinkan Anda membuat tipe baru dari Serikat. Nilai-nilai di Uni digunakan sebagai atribut dari tipe baru.

Misalnya, saya memiliki Serikat seperti ini:

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

Sekarang saya ingin membuat objek yang berisi informasi tentang semua kucing, saya bisa membuat tipe baru menggunakan nilai-nilai di CatName Union sebagai kunci.

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

Jika saya ingin memenuhi CatList ini, saya harus membuat objek seperti ini:

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

Anda mendapatkan keamanan tipe yang sangat kuat:

  • Jika saya lupa kucing, saya mendapatkan kesalahan.
  • Jika saya menambahkan kucing yang tidak diizinkan, saya mendapatkan kesalahan.
  • Jika nanti saya mengubah CatNames, saya mendapatkan kesalahan. Ini sangat berguna karena CatNames kemungkinan diimpor dari file lain, dan kemungkinan digunakan di banyak tempat.

Contoh nyata bereaksi dunia nyata.

Saya menggunakan ini baru-baru ini untuk membuat komponen Status. Komponen akan menerima prop status, dan kemudian membuat ikon. Saya telah menyederhanakan kode di sini untuk tujuan ilustrasi

Saya memiliki serikat pekerja seperti ini:

type Statuses = "failed" | "complete";

Saya menggunakan ini untuk membuat objek seperti ini:

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

Saya kemudian dapat membuat dengan menghancurkan elemen dari objek menjadi alat peraga, seperti:

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

Jika penyatuan Status kemudian diperpanjang atau diubah, saya tahu komponen Status saya akan gagal dikompilasi dan saya akan mendapatkan kesalahan yang bisa segera saya perbaiki. Ini memungkinkan saya untuk menambahkan status kesalahan tambahan ke aplikasi.

Perhatikan bahwa aplikasi aktual memiliki lusinan status kesalahan yang direferensikan di banyak tempat, jadi jenis keamanan ini sangat berguna.


Saya berasumsi sebagian besar waktu type Statuseshidup dalam mengetik TIDAK didefinisikan oleh Anda? Kalau tidak, saya bisa melihat sesuatu seperti antarmuka dengan enum menjadi lebih cocok kan?
Victorio Berra

Hai @victorio, saya tidak yakin bagaimana enum akan menyelesaikan masalah, Anda tidak mendapatkan kesalahan dalam enum jika Anda kehilangan kunci. Itu hanya pemetaan antara kunci dan nilai.
superluminary

1
Saya mengerti maksud Anda sekarang. Berasal dari C # kita tidak memiliki cara pintar untuk melakukan itu. Hal yang paling dekat adalah kamus Dictionary<enum, additional_metadata>. Tipe catatan adalah cara yang bagus untuk mewakili pola enum + metadata itu.
Victorio Berra
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.