Record type ใน typescript คืออะไร?


182

อะไรRecord<K, T>หมายถึงใน typescript?

typescript 2.1 แนะนำRecordประเภทที่อธิบายไว้ในตัวอย่าง:

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

ดูtypescript 2.1

และประเภทสินค้าทุกประเภทหน้ากล่าวRecordภายใต้ประเภทแมปมุ่งหน้าไปข้างReadonly, PartialและPickในสิ่งที่ดูเหมือนจะเป็นความหมายของมัน

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

อ่านอย่างเดียวบางส่วนและเลือกเป็น homomorphic ในขณะที่บันทึกไม่ได้ เงื่อนงำหนึ่งที่ Record ไม่ใช่ homomorphic คือมันไม่ใช้ประเภทอินพุตเพื่อคัดลอกคุณสมบัติจาก:

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

และนั่นคือมัน นอกจากราคาข้างต้นแล้วไม่มีการกล่าวถึงRecordในtypescriptlang.orgอีก

คำถาม

  1. ใครสามารถให้คำจำกัดความง่ายๆว่าอะไรRecordคืออะไร?

  2. เป็นRecord<K,T>เพียงวิธีการพูดว่า "คุณสมบัติทั้งหมดในวัตถุนี้จะมีประเภทT"? อาจไม่ได้คุณสมบัติทั้งหมดเนื่องจากKมีวัตถุประสงค์บางอย่าง ...

  3. ไม่Kคีย์เพิ่มเติมทั่วไปห้ามบนวัตถุที่ไม่ได้Kหรือไม่ได้ช่วยให้พวกเขาและเพียงแค่ระบุว่าคุณสมบัติของพวกเขาจะไม่เปลี่ยนT?

  4. ด้วยตัวอย่างที่กำหนด:

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

มันเหมือนกับตรงนี้ไหม:

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

6
คำตอบที่ 4 นั้นค่อนข้าง "ใช่" ดังนั้นน่าจะตอบคำถามอื่นของคุณได้
jcalz

คำตอบ:


210
  1. ใครสามารถให้คำจำกัดความง่ายๆว่าอะไรRecordคืออะไร?

Record<K, T>เป็นชนิดของวัตถุที่มีคุณสมบัติคีย์และมีทรัพย์สินมีค่าK Tนั่นคือkeyof Record<K, T>เทียบเท่ากับKและRecord<K, T>[K]เป็น (พื้น) Tเทียบเท่ากับ

  1. เป็นRecord<K,T>เพียงวิธีการพูดว่า "คุณสมบัติทั้งหมดในวัตถุนี้จะมีประเภทT"? อาจไม่ใช่วัตถุทั้งหมดเนื่องจากKมีวัตถุประสงค์บางอย่าง ...

ตามที่คุณทราบKมีวัตถุประสงค์ ... เพื่อ จำกัด คีย์คุณสมบัติเป็นค่าเฉพาะ หากคุณต้องการที่จะยอมรับกุญแจสตริงมูลค่าเป็นไปได้ทั้งหมดที่คุณสามารถทำสิ่งที่ชอบRecord<string, T>แต่วิธีการทำสำนวนที่ว่าคือการใช้ลายเซ็นดัชนี{ [k: string]: T }เช่น

  1. ไม่Kคีย์เพิ่มเติมทั่วไปห้ามบนวัตถุที่ไม่ได้Kหรือไม่ได้ช่วยให้พวกเขาและเพียงแค่ระบุว่าคุณสมบัติของพวกเขาจะไม่เปลี่ยนT?

มันไม่ตรงกับ "ห้าม" คีย์เพิ่มเติม: หลังจากทั้งหมดค่าทั่วไปจะได้รับอนุญาตให้มีคุณสมบัติที่ไม่ได้กล่าวถึงอย่างชัดเจนในประเภทของมัน ... แต่มันจะไม่รับรู้ว่ามีคุณสมบัติดังกล่าว:

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

และมันจะรักษาพวกเขาเป็นคุณสมบัติส่วนเกินซึ่งบางครั้งถูกปฏิเสธ:

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

และบางครั้งก็ยอมรับ:

const y = {a: "hey", b: "you"};
acceptR(y); // okay
  1. ด้วยตัวอย่างที่กำหนด:

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

    มันเหมือนกับตรงนี้ไหม:

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

ใช่

หวังว่าจะช่วย โชคดี!


1
เรียนรู้อย่างมากและคำถามที่ว่าทำไม "วิธีการทำเช่นนั้นคือใช้ลายเซ็นของดัชนี" ไม่ใช่ Record one? ฉันไม่พบข้อมูลที่เกี่ยวข้องเกี่ยวกับ "วิธีการใช้สำนวน" นี้
legend80s

2
คุณสามารถใช้Record<string, V>หมายถึง{[x: string]: V}ถ้าคุณต้องการ; ฉันอาจทำสิ่งนี้ด้วยตัวเอง เวอร์ชันของลายเซ็นดัชนีนั้นตรงกว่า: มันเป็นชนิดเดียวกัน แต่ในอดีตเป็นนามแฝงของประเภทที่แมปซึ่งจะประเมินเป็นลายเซ็นของดัชนีในขณะที่อันหลังนั้นเป็นเพียงลายเซ็นของดัชนีโดยตรง ทุกอย่างเท่าเทียมกันฉันจะแนะนำอย่างหลัง ในทำนองเดียวกันฉันจะไม่ใช้Record<"a", string>แทน{a: string}เว้นแต่จะมีเหตุผลเชิงบริบทที่น่าสนใจทำเช่นนั้น
jcalz

1
" ทุกอย่างเท่าเทียมกันฉันจะแนะนำอย่างหลัง " ทำไมถึงเป็นเช่นนั้น pre-typescript ของฉันเห็นด้วย แต่ฉันรู้ว่าคนก่อนหน้านี้จะมีมากขึ้นอืมแสดงความคิดเห็นด้วยตนเองสำหรับผู้ที่มาจากฝั่ง C # เช่นกันและไม่เลวร้ายยิ่งกว่า คุณเพียงแค่สนใจที่จะข้ามขั้นตอนการแพร่กระจายสำหรับการสร้างเหล่านั้น?
ruffin

1
เพียงความเห็นของฉัน: พฤติกรรมของเหมาะสมRecord<string, V>เท่านั้นถ้าคุณรู้อยู่แล้วว่าลายเซ็นดัชนีทำงานใน TypeScript เช่นรับx: Record<string, string>, x.fooเห็นได้ชัดว่าจะเป็นstringที่รวบรวมเวลา string | undefinedแต่ในความเป็นจริงมีแนวโน้มที่จะ นี่เป็นช่องว่างในการ--strictNullChecksทำงาน (ดู# 13778 ) ฉันอยากจะมีคนที่มาใหม่จัดการกับ{[x: string]: V}โดยตรงแทนการคาดหวังว่าพวกเขาจะปฏิบัติตามห่วงโซ่จากRecord<string, V>ผ่าน{[P in string]: V}ไปสู่พฤติกรรมที่ลายเซ็นดัชนี
jcalz

ฉันต้องการชี้ให้เห็นว่าเหตุผลประเภทสามารถกำหนดเป็นชุดของค่าทั้งหมดที่มีอยู่ในประเภท จากการตีความนี้ฉันคิดว่า Record <string, V> มีเหตุผลในฐานะที่เป็นนามธรรมเพื่อทำให้โค้ดง่ายขึ้นแทนที่จะวางค่าที่เป็นไปได้ทั้งหมด มันคล้ายกับตัวอย่างในเอกสารประเภทยูทิลิตี้: บันทึก <T, V> โดยที่ T = 'a' | 'b' | 'ค'. ไม่เคยทำการบันทึก <'a', string> ไม่ใช่ตัวอย่างตัวนับที่ดีเพราะมันไม่เป็นไปตามรูปแบบเดียวกัน นอกจากนี้ยังไม่เพิ่มเพื่อนำมาใช้ใหม่หรือลดความซับซ้อนของรหัสผ่าน abstraction ตามตัวอย่างอื่น ๆ
Scott Leonard

68

บันทึกช่วยให้คุณสร้างประเภทใหม่จากสหภาพ ค่าในสหภาพถูกใช้เป็นคุณลักษณะของชนิดใหม่

ตัวอย่างเช่นฉันมีสหภาพแบบนี้:

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

ตอนนี้ฉันต้องการสร้างวัตถุที่มีข้อมูลเกี่ยวกับแมวทั้งหมดฉันสามารถสร้างชนิดใหม่โดยใช้ค่าใน CatName Union เป็นคีย์

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

หากฉันต้องการ CatList นี้ฉันต้องสร้างวัตถุเช่นนี้

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

คุณจะได้รับความปลอดภัยประเภทที่แข็งแกร่งมาก:

  • ถ้าฉันลืมแมวฉันได้รับข้อผิดพลาด
  • หากฉันเพิ่มแมวที่ไม่ได้รับอนุญาตฉันได้รับข้อผิดพลาด
  • หากภายหลังฉันเปลี่ยน CatNames ฉันได้รับข้อผิดพลาด สิ่งนี้มีประโยชน์อย่างยิ่งเนื่องจาก CatNames อาจนำเข้าจากไฟล์อื่นและมีแนวโน้มที่จะใช้ในหลาย ๆ ที่

ตัวอย่างการตอบโต้โลกแห่งความจริง

ฉันใช้สิ่งนี้เมื่อเร็ว ๆ นี้เพื่อสร้างองค์ประกอบสถานะ องค์ประกอบจะได้รับการแสดงสถานะจากนั้นแสดงไอคอน ฉันทำให้โค้ดค่อนข้างง่ายที่นี่เพื่อจุดประสงค์ในการอธิบาย

ฉันมีสหภาพเช่นนี้:

type Statuses = "failed" | "complete";

ฉันใช้สิ่งนี้เพื่อสร้างวัตถุเช่นนี้

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

จากนั้นฉันสามารถแสดงผลโดยการทำลายองค์ประกอบจากวัตถุไปยังอุปกรณ์ประกอบฉากเช่น:

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

หากการรวมสถานะ Statuses ถูกขยายหรือเปลี่ยนแปลงในภายหลังฉันรู้ว่าองค์ประกอบสถานะของฉันจะไม่สามารถรวบรวมและฉันจะได้รับข้อผิดพลาดที่ฉันสามารถแก้ไขได้ทันที สิ่งนี้ทำให้ฉันสามารถเพิ่มสถานะข้อผิดพลาดเพิ่มเติมให้กับแอพ

โปรดทราบว่าแอปที่เกิดขึ้นจริงมีสถานะข้อผิดพลาดหลายสิบที่อ้างอิงในหลายสถานที่ดังนั้นความปลอดภัยประเภทนี้จึงมีประโยชน์อย่างยิ่ง


ฉันคิดว่าเวลาส่วนใหญ่type Statusesอยู่ในการพิมพ์ที่คุณไม่ได้กำหนดไว้? มิฉะนั้นฉันสามารถเห็นบางสิ่งบางอย่างเช่นอินเทอร์เฟซที่มี Enum ที่เหมาะสมกว่าใช่ไหม
Victorio Berra

สวัสดี @victorio ฉันไม่แน่ใจว่า enum จะแก้ปัญหาอย่างไรคุณไม่ได้รับข้อผิดพลาดใน enum หากคุณพลาดกุญแจ มันเป็นเพียงการจับคู่ระหว่างคีย์และค่าต่างๆ
superluminary

1
ฉันเห็นสิ่งที่คุณหมายถึงตอนนี้ มาจาก C # เราไม่มีวิธีที่ฉลาดในการทำเช่นนั้น Dictionary<enum, additional_metadata>สิ่งที่ใกล้จะเป็นพจนานุกรมของ ประเภทระเบียนเป็นวิธีที่ยอดเยี่ยมในการแสดงรูปแบบ enum + ข้อมูลเมตา
Victorio Berra
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.