Làm cho tất cả các thuộc tính trong giao diện Typecript là tùy chọn


96

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 đăng, trong đó đối tượng nội dung 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 PostDraftđối tượng có một đối tượng nội dung một phần trong khi vẫn kiểm tra các loại thuộc tính có ở đó (vì vậy tôi không muốn hoán đổi nó với any).

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

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

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


Hôm nay bạn cần tạo giao diện thứ hai đó theo cách thủ công, mặc dù điều này có thể thay đổi trong tương lai gần: hãy kiểm tra vấn đề loại từng phần trên repo ts nếu bạn quan tâm.
— Alex Guerra

Câu trả lời:


208

Điều này không thể xảy ra trong TypeScript <2.1 nếu không tạo giao diện bổ sung với các thuộc tính tùy chọn; tuy nhiên, điều này có thể thực hiện được bằng cách sử dụng các kiểu được ánh xạ trong TypeScript 2.1+.

Để thực hiện việc này, hãy sử dụng Partial<T>kiểu 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 assetlà tùy chọn, cho phép bạn thực hiện những việc sau:

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

Trong khoảng Partial<T>

Partial<T>được định nghĩa là một loại được ánh xạ làm cho mọi thuộc tính trong loại được cung cấp là tùy chọn (sử dụng ?mã thông báo).

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

Đọc thêm về các loại được ánh xạ tại đây và trong sổ tay .


@david có thể đi theo cách khác không? Để đánh dấu các trường trước đây là một phần theo yêu cầu? Và cũng có thể làm điều đó trên cơ sở từng lĩnh vực?
— Tony

1
@Tony có, nó sẽ khả thi với các loại điều kiện trong TS 2.8 (xem Required<T>).
— David Sherret

2
omg đây là một giải pháp sạch sẽ. Ước gì tôi biết điều này sớm hơn một chút
— CiriousJoker

3
Tiếp tục ngạc nhiên trước tư tưởng đã đi vào TS ... của Microsoft không hơn không kém. Ước gì ý định tương tự cũng đến với sự phát triển trình duyệt web của họ (-_-)
— greatKing

4

Các thuộc tính trong giao diện là tùy chọn không phải là tùy chọn, bạn không thể sử dụng cùng một giao diện một lần tùy chọn và một lần khi phải.

Những gì bạn có thể làm là có một giao diện với các thuộc tính tùy chọn cho AssetDraftvà 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 theo những cách khác hoặc gặp 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 json nhận được từ máy chủ (hoặc cái gì đó tương tự), các giao diện đại diện cho dữ liệu json và các lớp là mô hình thực tế được xây dựng bằng cách sử dụng json làm giá trị ban đầu.


Có, bạn có thể..như đã nêu trong câu trả lời của David Sherret ... bạn phải sử dụng biến kiểu chung để chuyển giao diện của bạn dưới dạng Phần <T> ..T là giao diện của bạn.
— hơnKing

@greaterKing bây giờ nó có thể và dễ dàng
— Nitzan Tomer

1

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

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

interface AssetDraft extends Partial<Asset> {}

0

Ngoài câu trả lời của David Sherret chỉ cần một vài dòng từ phía tôi làm thế nào nó có thể được thực hiện trực tiếp mà không cần Partial<T>nhập để hiểu rõ hơn về chủ đề này.

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

interface IPost {
  asset: IAsset;
}

interface IPostDraft {
  asset: { [K in keyof IAsset]?: IAsset[K] };
}

const postDraft: IPostDraft = {
  asset: {
    usage: 123
  }
};

-1

Làm thế nào về lực đúc một đối tượng trống rỗng, ví dụ

const draft = <PostDraft>{}
draft.id = 123
draft.internal_id = 456
draft.usage = 789

Nếu bạn thực sự cần điều này thì bạn luôn có thể tạo giao diện d.ts từ một mẫu tạo cả thuộc tính tùy chọn và thuộc tính đã nhập.

Như Nitzan đã chỉ ra, các thuộc tính giao diện Typecript là tùy chọn hoặc không

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 cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.