Tìm kiếm trang web

react redux - Giao diện Typescript để giới hạn đối tượng dựa trên giá trị khóa


Tôi mới bắt đầu với Typescript và đã thử nghịch thử. Tôi có đoạn mã này đang gặp lỗi (đã rút gọn cho đơn giản). Nó cuối cùng là cho một action/reducer của redux, đó là lý do tại sao nó hơi rối một chút.

interface StoreType {
  bool: boolean,
  num: number,
}

interface PayloadType {
  key: keyof StoreType,
  value: ???
}

let test: PayloadType = {
  key: "bool",
  value: 3 // should ideally fail if value is not a boolean 
}

var obj: StoreType = { bool: true, num: 3 }

obj[test.key] = test.value // ts(2322) Type 'number' is not assignable to type 'never'

Câu hỏi hai phần:

  1. Có thể nào tinh chỉnh value trong PayloadType sao cho test.value có kiểu boolean nếu test.key == "bool", và có kiểu number nếu test.key === "num" không? Tôi đã thử value: StoreType[keyof StoreType], nhưng không đủ ràng buộc :(

  2. Có cách nào để làm cho phép gán obj[test.key]=test.value an toàn về kiểu và không lỗi không? Tôi đã thấy lỗi trong https://github.com/microsoft/TypeScript/issues/31663, nhưng tôi không chắc chắn về cách tiếp tục và tôi không biết liệu điều đó có liên quan đến câu hỏi 1 hay không.

Nếu có điều gì cơ bản mà tôi đã bỏ lỡ, tôi sẽ không ngạc nhiên.

Cảm ơn!

Câu trả lời #1

Định nghĩa kiểu key trong PayloadType như một tham số kiểu tổng quát K với ràng buộc keyof StoreType sẽ giải quyết vấn đề:

interface PayloadType<K extends keyof StoreType> {
    key: K,
    value: StoreType[K]
}

let test: PayloadType<"bool"> = {
    key: "bool",
    value: true // ✅
}

let testError: PayloadType<"bool"> = {
    key: "bool",
    value: 3 // 💣
}

var obj: StoreType = { bool: true, num: 3 }

obj[test.key] = test.value

StoreType[keyof StoreType] không hoạt động cho ví dụ này, bởi vì keyof StoreType sẽ giải quyết thành một kiểu hợp nhất "bool" | "num". Và một tra cứu của "bool" | "num" trong StoreType sẽ giải quyết thành tất cả các kiểu trả về có thể có number | boolean của các khóa này.

Bằng cách khai báo kiểu key dưới dạng tham số kiểu tổng quát K cùng với PayloadType<"bool">, bạn bày tỏ rằng một khóa cụ thể của StoreType (không phải một hợp nhất) được mong đợi.

Sân chơi

Câu trả lời #2

@ford04 có vẻ là một câu trả lời tốt cho câu hỏi mà bạn đang hỏi, nhưng không phải cho điều mà bạn thực sự muốn đạt được.

Không có lý do nào (hoặc tôi không thấy) để có thông tin kiểu rõ ràng trong payload của bạn. Tôi sẽ nói rằng bạn đang tiết lộ một số chi tiết kỹ thuật ở nơi mà dữ liệu miền nên được tiết lộ. Vì vậy, đối với tôi, đó là cách tiếp cận sai.

Ý nghĩa trong trường hợp như vậy là có bộ phân biệt dưới dạng thông tin miền:

type ActionType = 'add_user' | 'get_user' | 'add_comment' | 'get_comment' // example action types

// type constructor to save us typing
type Action<T extends ActionType, P> = {
  type: T,
  payload: P
}

// specific actions
type UserId = number; // just for convenience and readability
type GetUserAction = Action<'get_user', UserId>

type User = {name: string} // example payload type
type AddUserAction = Action<'add_user', User>

// action creator
const getUser = (id: UserId):GetUserAction   => ({type: 'get_user', payload: id});

Payload cũng có thể là kiểu chính như bạn có thể thấy trong ví dụ trên với hành động get_user. Cách tiếp cận này an toàn về kiểu, bạn không thể gửi hành động get_user với loại payload sai. Hy vọng điều này hữu ích.