Tìm kiếm trang web

Làm cho tất cả các thuộc tính trong một interface Typescript trở nên tùy chọn


Tôi có một giao diện trong ứng dụng của mình:

interface Asset {
  id: string;
  internal_id: string;
  usage: number;
}

đó là một phần của giao diện bài đăng:

interface Post {
  asset: Asset;
}

Tôi cũng có một giao diện dành cho bản nháp bài đăng, nơi đối tượng tài sản có thể chỉ được xây dựng một phần

interface PostDraft {
  asset: Asset;
}

Tôi muốn cho phép một đối tượng PostDraft có một đối tượng tài sản một phần trong khi vẫn kiểm tra kiểu trên các thuộc tính hiện có (vì vậy tôi không muốn chỉ thay thế nó bằng any).

Về cơ bản tôi muốn có một cách để có thể tạo ra những điều sau:

interface AssetDraft {
  id?: string;
  internal_id?: string;
  usage?: number;
}

mà không hoàn toàn định nghĩa lại giao diện Asset. Có cách nào để làm điều này không? Nếu không, cách thông minh để sắp xếp các kiểu của tôi trong tình huống này là gì?

Câu trả lời #1

Điều này không khả thi trong TypeScript < 2.1 mà không tạo một interface bổ sung với các thuộc tính tùy chọn; tuy nhiên, điều này khả thi bằng cách sử dụng các kiểu ánh xạ trong TypeScript 2.1 trở lên.

Để làm điều này, hãy sử dụng kiểu Partial<T> mà TypeScript cung cấp theo mặc định.

interface PostDraft {
    asset: Partial<Asset>;
}

Bây giờ tất cả các thuộc tính trên asset đều là tùy chọn, điều này sẽ cho phép bạn thực hiện những việc sau:

const postDraft: PostDraft = {
    asset: {
        id: "some-id"
    }
};

Về Partial<T>

Partial<T> được định nghĩa là một kiểu được ánh xạ mà làm cho mọi thuộc tính trong kiểu được cung cấp trở thành tùy chọn (sử dụng ký hiệu ?).

type Partial<T> = {
    [P in keyof T]?: T[P];
};

Đọc thêm về các loại ánh xạ ở đây và trong cẩm nang.

Sâu Từng Phần

Nếu bạn muốn một triển khai một phần hoạt động đệ quy trên các đối tượng thì bạn có thể sử dụng kiểu sau trong TS 4.1+:

type DeepPartial<T> = {
    [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

Câu trả lời #2

Nếu tôi muốn có một giao diện AssetDraft rõ ràng, tôi sẽ sử dụng kết hợp extends và Partial:

interface Asset {
  id: string;
  internal_id: string;
  usage: number;
}

interface AssetDraft extends Partial<Asset> {}

Câu trả lời #3

Các thuộc tính trong giao diện hoặc là tùy chọn hoặc không, bạn không thể sử dụng cùng một giao diện vừa là tùy chọn vừa là bắt buộc.

Những gì bạn có thể làm là tạo một giao diện với các thuộc tính tùy chọn cho AssetDraft và sau đó là một lớp với các thuộc tính bắt buộc cho Asset:

interface AssetDraft {
    id?: string;
    internal_id?: string;
    usage?: number;
}

class Asset {
    static DEFAULT_ID = "id";
    static DEFAULT_INTERNAL_ID = "internalid";
    static DEFAULT_USAGE = 0;

    id: string;
    internal_id: string;
    usage: number;

    constructor(draft: AssetDraft) {
        this.id = draft.id || Asset.DEFAULT_ID;
        this.internal_id = draft.internal_id || Asset.DEFAULT_INTERNAL_ID;
        this.usage = draft.usage || Asset.DEFAULT_USAGE;
    }
}

Các giá trị mặc định ở đây là các thành viên tĩnh, nhưng bạn có thể lấy chúng bằng cách khác hoặc ném ra lỗi trong trường hợp chúng bị thiếu.

Tôi thấy cách này rất thoải mái khi làm việc với các json nhận được từ server (hoặc những thứ tương tự), các interface đại diện cho dữ liệu json và các lớp là các mô hình thực tế được xây dựng sử dụng json làm giá trị khởi tạo.

Bài viết liên quan