Tìm kiếm trang web

typescript - Tạo một kiểu được xây dựng từ các giá trị của một đối tượng


Giả sử tôi có một đối tượng như thế này:

const MenuOptions = {
    Project: ["new", "open", "close"],
    File: ["create", "edit"]
    // ...
}

Các giá trị của đối tượng đều là duy nhất.

Tôi cũng muốn định nghĩa một phương thức để cho phép người dùng nhấp vào một tùy chọn trong menu:

function clickOnMenuOption(option: string) { ... }

Giá trị của tham số option chỉ có thể là một trong các giá trị new/open/close/create/edit/....

Có thể định nghĩa một kiểu MenuOption dựa trên MenuOptions không?

type MenuOption = ???

Câu trả lời #1

Nếu bạn chỉ khai báo MenuOptions như trên, trình biên dịch sẽ có xu hướng suy diễn các kiểu giá trị chỉ là string[] thay vì một bộ giá trị cố định (tuple) của các hằng số. Nếu bạn cần truy cập vào các giá trị cố định đó, bạn sẽ cần thay đổi cách khai báo đối tượng của mình. Khi TypeScript 3.4 ra mắt vào tháng 3 năm 2019, bạn sẽ có thể sử dụng ngữ cảnh const để gợi ý cho trình biên dịch suy diễn kiểu hẹp nhất có thể, như sau:

const MenuOptions = {
  Project: ["new", "open", "close"],
  File: ["create", "edit"]
  // ...
} as const; // only works in TS3.4+

Cho đến lúc đó, bạn có thể sử dụng một hàm trợ giúp như sau để hướng dẫn việc suy luận:

type Narrowable = string | number | boolean | undefined | null | void | {};   
const tuple = <T extends Narrowable[]>(...t: T)=> t;    

const MenuOptions = {
  Project: tuple("new", "open", "close"),
  File: tuple("create", "edit")
  // ...
};

Trong cả hai trường hợp, loại bạn muốn được đưa ra ở trên là

type MenuOption = (typeof MenuOptions)[keyof typeof MenuOptions][number];

Hãy phân tách điều đó thành các bước và đặt tên cho các kiểu trung gian. Bạn đang lấy kiểu của MenuOptions:

type TypeofMenuOptions = typeof MenuOptions;
// type TypeofMenuOptions = {
//   Project: ["new", "open", "close"];
//   File: ["create", "edit"];
// }

tìm kiếm một hợp nhất của các loại giá trị của nó

type MenuOptionsValues = TypeofMenuOptions[keyof TypeofMenuOptions];
// type MenuOptionsValues = ["new", "open", "close"] | ["create", "edit"]

và tra cứu các thuộc tính được đánh số chỉ mục của nó (Nếu bạn có một kiểu (A | B)[K], nó được đánh giá là A[K] | B[K])

type MenuOption = MenuOptionsValues[number];
// type MenuOption = "new" | "open" | "close" | "create" | "edit"

Điều này để lại cho bạn tập hợp hợp nhất của các hằng chuỗi mà bạn đang tìm kiếm.

Được rồi, hy vọng điều đó giúp ích. Chúc may mắn!

Câu trả lời #2

Tôi không biết kinh nghiệm của bạn với TS như thế nào, nhưng bạn dường như mắc phải những lỗi điển hình của người mới dùng TS (nhầm lẫn giữa kiểu với giá trị), xin lỗi nếu tôi nhầm.

Mã TS theo thành ngữ nên trông như thế này:

type Option = "new" | "open" | "close" | "create" | "edit"

interface MenuOptions {
    Project: Option[],
    File: Option[]
}

const menuOptions: MenuOptions = {
    Project: ["new", "open", "close"],
    File: ["create", "edit"]
    // ...
}

function clickOnMenuOption(option: Option) {  }