Loại bản ghi trong bản thảo là gì?


182

Điều gì có Record<K, T>nghĩa trong bản đánh máy?

Bản đánh máy 2.1 đã giới thiệu Recordloại, mô tả nó trong một ví dụ:

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

xem bản đánh máy 2.1

các loại chi tiết trang đề cập đến Recorddưới loại ánh xạ hướng bên cạnh Readonly, PartialPick, trong những gì dường như là định nghĩa của nó:

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

Chỉ đọc, một phần và Pick là đồng hình trong khi Record thì không. Một manh mối cho thấy Bản ghi không đồng hình là nó không lấy loại đầu vào để sao chép các thuộc tính từ:

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

Và đó là nó. Ngoài các trích dẫn ở trên, không có đề cập nào khác Recordvề typecriptang.org .

Câu hỏi

  1. Ai đó có thể đưa ra một định nghĩa đơn giản về những gì Recordlà?

  2. Có phải Record<K,T>chỉ là một cách để nói "tất cả các thuộc tính trên đối tượng này sẽ có loại T"? Có lẽ không phải tất cả tài sản, vì Kcó một số mục đích ...

  3. Kchung chung cấm các khóa bổ sung trên đối tượng không K, hoặc nó cho phép chúng và chỉ cho biết rằng các thuộc tính của chúng không được chuyển đổi thànhT ?

  4. Với ví dụ đã cho:

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

Có chính xác như thế này không?:

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

6
Câu trả lời cho 4. là khá nhiều "có", vì vậy có lẽ nên trả lời các câu hỏi khác của bạn.
jcalz

Câu trả lời:


210
  1. Ai đó có thể đưa ra một định nghĩa đơn giản về những gì Recordlà?

A Record<K, T>là một loại đối tượng có khóa thuộc tính Kvà có giá trị thuộc tính T. Đó là, keyof Record<K, T>tương đương với K, và Record<K, T>[K](về cơ bản) tương đương với T.

  1. Có phải Record<K,T>chỉ là một cách để nói "tất cả các thuộc tính trên đối tượng này sẽ có loại T"? Có lẽ không phải tất cả các đối tượng, vì Kcó một số mục đích ...

Như bạn lưu ý, Kcó một mục đích ... để giới hạn các khóa thuộc tính ở các giá trị cụ thể. Nếu bạn muốn chấp nhận tất cả các khóa có giá trị chuỗi có thể, bạn có thể làm một cái gì đó giống như Record<string, T>, nhưng cách làm thành ngữ đó là sử dụng chữ ký chỉ mục như thế nào { [k: string]: T }.

  1. Có phải Kchung chung cấm các khóa bổ sung trên đối tượng không K, hoặc nó cho phép chúng và chỉ cho biết rằng các thuộc tính của chúng không được chuyển đổi thànhT ?

Nó không chính xác "cấm" các khóa bổ sung: xét cho cùng, một giá trị thường được cho phép có các thuộc tính không được đề cập rõ ràng trong loại của nó ... nhưng nó sẽ không nhận ra rằng các thuộc tính đó tồn tại:

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

và nó sẽ coi chúng là những đặc tính dư thừa đôi khi bị từ chối:

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

và đôi khi được chấp nhận:

const y = {a: "hey", b: "you"};
acceptR(y); // okay
  1. Với ví dụ đã cho:

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

    Có chính xác như thế này không?:

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

Đúng!

Mong rằng sẽ giúp. Chúc may mắn!


1
Đã học được rất nhiều và một câu hỏi tại sao "cách thức thành ngữ đó là sử dụng chữ ký chỉ mục" chứ không phải là Bản ghi? Tôi không tìm thấy bất kỳ thông tin liên quan nào về "cách thành ngữ" này.
Legend80s

2
Bạn có thể sử dụng Record<string, V>để có nghĩa là {[x: string]: V}nếu bạn muốn; Tôi thậm chí có thể tự làm điều này. Phiên bản chữ ký chỉ mục trực tiếp hơn: chúng cùng loại, nhưng phiên bản trước là bí danh loại thuộc loại ánh xạ để đánh giá chữ ký chỉ mục, trong khi phiên bản sau chỉ là chữ ký chỉ mục trực tiếp. Tất cả những thứ khác đều bằng nhau, tôi muốn giới thiệu cái sau. Tương tự như vậy tôi sẽ không sử dụng Record<"a", string>thay cho {a: string}trừ khi có một số lý do bối cảnh hấp dẫn khác làm như vậy.
jcalz

1
" Tất cả những thứ khác đều bằng nhau, tôi muốn giới thiệu cái sau. " Tại sao vậy? Ví dụ, bản thân trước bản đánh máy của tôi đồng ý, nhưng tôi biết cái trước sẽ nhiều hơn, ừm, tự nhận xét cho những người đến từ phía C #, chẳng hạn, và không tệ hơn cho những người viết JavaScript. Bạn chỉ quan tâm đến việc bỏ qua bước dịch chuyển cho các cấu trúc đó?
ruffin

1
Theo ý kiến ​​của tôi: hành vi của Record<string, V>chỉ có ý nghĩa nếu bạn đã biết cách chữ ký chỉ mục hoạt động trong TypeScript. Ví dụ, được đưa ra x: Record<string, string>, x.foorõ ràng sẽ là một stringthời gian biên dịch, nhưng trong thực tế có khả năng là string | undefined. Đây là một lỗ hổng trong cách --strictNullCheckshoạt động (xem # 13778 ). Tôi muốn có những người mới đối phó với {[x: string]: V}trực tiếp thay vì chờ đợi họ làm theo chuỗi từ Record<string, V>qua {[P in string]: V}đến hành vi chữ ký số.
jcalz

Tôi muốn chỉ ra rằng một loại logic có thể được định nghĩa là một tập hợp tất cả các giá trị có trong loại. đưa ra cách giải thích này tôi nghĩ Record <string, V> là hợp lý như một sự trừu tượng hóa để đơn giản hóa mã thay vì đặt ra tất cả các giá trị có thể. Nó tương tự như ví dụ trong tài liệu loại tiện ích: Ghi <T, V> trong đó loại T = 'a' | 'b' | 'C'. Không bao giờ thực hiện Bản ghi <'a', chuỗi> không phải là một ví dụ phản biện tốt, vì nó không theo cùng một mẫu. Nó cũng không thêm để sử dụng lại hoặc đơn giản hóa mã thông qua sự trừu tượng hóa như các ví dụ khác làm.
Scott Leonard

68

Bản ghi cho phép bạn tạo một loại mới từ Liên minh. Các giá trị trong Liên minh được sử dụng làm thuộc tính của loại mới.

Ví dụ: giả sử tôi có một Liên minh như thế này:

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

Bây giờ tôi muốn tạo một đối tượng chứa thông tin về tất cả các con mèo, tôi có thể tạo một loại mới bằng cách sử dụng các giá trị trong Liên minh CatName làm khóa.

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

Nếu tôi muốn thỏa mãn CatList này, tôi phải tạo một đối tượng như thế này:

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

Bạn nhận được loại an toàn rất mạnh:

  • Nếu tôi quên một con mèo, tôi nhận được một lỗi.
  • Nếu tôi thêm một con mèo không được phép, tôi sẽ gặp lỗi.
  • Nếu sau này tôi thay đổi CatNames, tôi sẽ gặp lỗi. Điều này đặc biệt hữu ích vì CatNames có thể được nhập từ một tệp khác và có thể được sử dụng ở nhiều nơi.

Ví dụ React trong thế giới thực.

Tôi đã sử dụng điều này gần đây để tạo ra một thành phần Status. Thành phần này sẽ nhận được một trạng thái chống đỡ, và sau đó kết xuất một biểu tượng. Tôi đã đơn giản hóa mã khá nhiều ở đây cho mục đích minh họa

Tôi đã có một liên minh như thế này:

type Statuses = "failed" | "complete";

Tôi đã sử dụng điều này để tạo ra một đối tượng như thế này:

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

Sau đó tôi có thể kết xuất bằng cách hủy một phần tử từ đối tượng thành các đạo cụ, như vậy:

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

Nếu liên kết Trạng thái sau đó được mở rộng hoặc thay đổi, tôi biết thành phần Trạng thái của tôi sẽ không biên dịch được và tôi sẽ gặp lỗi mà tôi có thể khắc phục ngay lập tức. Điều này cho phép tôi thêm các trạng thái lỗi bổ sung vào ứng dụng.

Lưu ý rằng ứng dụng thực tế có hàng tá trạng thái lỗi được tham chiếu ở nhiều nơi, vì vậy loại an toàn này cực kỳ hữu ích.


Tôi cho rằng hầu hết thời gian type Statusessống trong các kiểu chữ KHÔNG được xác định bởi bạn? Nếu không, tôi có thể thấy một cái gì đó giống như một giao diện với enum là phù hợp hơn phải không?
Victorio Berra

Xin chào @victorio, tôi không chắc enum sẽ giải quyết vấn đề như thế nào, bạn không gặp lỗi trong enum nếu bạn bỏ lỡ khóa. Nó chỉ là một ánh xạ giữa các khóa và giá trị.
siêu sáng

1
Tôi thấy những gì bạn có nghĩa là bây giờ. Đến từ C #, chúng tôi không có cách thông minh để làm điều đó. Điều gần nhất sẽ là một từ điển của Dictionary<enum, additional_metadata>. Kiểu Bản ghi là một cách tuyệt vời để biểu diễn mẫu siêu dữ liệu enum + đó.
Victorio Berra
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.