Tìm kiếm trang web

types - Ghi đè các thuộc tính của một interface trong TypeScript


Tôi biết rằng việc ghi đè các thuộc tính của một giao diện trong một giao diện mở rộng, thay đổi loại của chúng, là bị cấm.

Tôi đang tìm một giải pháp thay thế cho phép tôi không phải sao chép nội dung của giao diện đầu tiên (nó khá lớn).

Dưới đây là cách tiếp cận ngây thơ đầu tiên của tôi. Với giao diện cơ sở đó:

interface OrginalInterface {
    title?: string;
    text?: string;
    anotherProperty?: SomeType;
    // lots of other properties
}

Giao diện này được định nghĩa trong một thư viện. Tôi không thể sửa đổi nó (ví dụ: thêm generics) chỉ để đáp ứng nhu cầu của tôi trong giao diện mở rộng.

Trong giao diện mở rộng, được sử dụng bởi một thư viện bao bọc (của tôi), tôi muốn tái sử dụng giao diện hiện có, đồng thời làm cho một số trường có kiểu khác nhau:

interface ExtendedInterface extends OriginalInterface {
    title?: string | ReactElement<any>;
    text?: string | ReactElement<any>;
}

Nhưng điều này là không thể.

error TS2430: Interface 'ExtendedInterface' incorrectly extends interface 'OriginalInterface'.
  Types of property 'title' are incompatible.
    Type 'ReactElement<any>' is not assignable to type 'string'.

Tôi cũng đã thử gộp hai giao diện lại với nhau:

type Extended = OriginalInterface & NewInterfaceWithOverridingPropertiesOnly;

Mặc dù điều này vẫn biên dịch được, nhưng nó không hoạt động. Nếu bạn khai báo một biến với loại này, bạn chỉ có thể gán các đối tượng có cấu trúc tương thích với OriginalInterface.

Tôi cảm thấy như hệ thống kiểu của TypeScript không cung cấp cho tôi bất kỳ cách nào khác để thể hiện nhu cầu của mình là khai báo một kiểu mới được dẫn xuất từ OriginalInterface. Tôi không cần kiểu mới có thể gán cho OriginalInterface; tôi chỉ cần nó tái sử dụng hầu hết các thuộc tính của OriginalInterface.

Tôi cần một thứ gì đó giống như các kiểu ánh xạ với một điều kiện về những thuộc tính nào bị ảnh hưởng. Có thể là các kiểu Điều kiện từ TypeScript 2.8 phiên bản thử nghiệm? Hoặc tôi nên sao chép nội dung của giao diện đầu tiên?

Câu trả lời #1

CẬP NHẬT, 2018-08

TypeScript 2.8 đã giới thiệu Exclude<T, U> hoạt động giống như Diff<T, U> được định nghĩa dưới đây cho tất cả các kiểu (không chỉ các kiểu khóa). Bạn chắc chắn nên sử dụng Exclude<> thay vì Diff<> nếu bạn đang sử dụng TypeScript 2.8 hoặc cao hơn.

Ngoài ra, trong TypeScript 2.9, keyof any đã được mở rộng từ string thành string | number | symbol, vì vậy Diff<T, U> dưới đây gây ra lỗi có thể được sửa bằng cách thay Diff<T extends string, U extends string> thành Diff<T extends keyof any, U extends keyof any>. Thay đổi này đã được thực hiện ở dưới đây.


CÂU TRẢ LỜI NGUYÊN BẢN, 2018-03

Vâng, các kiểu điều kiện sẽ cho phép điều này, mặc dù bạn cũng có thể có được hành vi này mà không cần chúng.

Ý tưởng là kéo các thuộc tính ra khỏi giao diện gốc và sau đó thay thế chúng bằng các thuộc tính mới. Như thế này:

type Diff<T extends keyof any, U extends keyof any> = 
  ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;

interface OriginalInterface {
  title?: string;
  text?: string;
  anotherProperty?: SomeType;
  // lots of other properties
}
interface Extension {
  title?: string | ReactElement<any>;
  text?: string | ReactElement<any>;
}

interface ExtendedInterface extends Overwrite<OriginalInterface, Extension> {};

const ext: ExtendedInterface = {};  // okay, no required properties
ext.text; // string | ReactElement<any> | undefined
ext.title; // string | ReactElement<any> | undefined
ext.anotherProperty; // SomeType | undefined

EDIT: Tôi đã thay đổi định nghĩa về Ghi đè để tôn trọng trạng thái tùy chọn/bắt buộc của các thuộc tính từ T có khóa không có trong U.

Tôi nghĩ điều này có hành vi mà bạn muốn. Hy vọng điều đó hữu ích; chúc may mắn!

Câu trả lời #2

Sử dụng TypeScript Omit:

Omit<T,K>

Xây dựng một kiểu bằng cách chọn tất cả các thuộc tính từ T và sau đó loại bỏ K

// original interface
interface A {
  a: number;
  b: number; // we want string type instead of number
}

// Remove 'b'
type BTemp = Omit<A, 'b'>;

// extends A (BTemp) and redefine b
interface B extends BTemp {
  b: string;
}

const a: B = {
  a: 5,
  b: 'B'
}

Bài viết liên quan