Tìm kiếm trang web

reactjs - Mở rộng một component MUI gây lỗi TS về việc thiếu thuộc tính "css"


Chúng tôi đã mở rộng thành phần SnackbarContent với thành phần tùy chỉnh của chúng tôi (MySnackbarContent):

export interface MySnackbarContentProps extends Omit<SnackbarContentProps, 'variant'> {
  variant?: MyCustomVariant;
  type?: MyCustomType;
  banner?: boolean;
  // ...
}

const MySnackbarContent = forwardRef<HTMLElement, MySnackbarContentProps>(props: MySnackbarContentProps, ref) => {
  const { variant = 'normal', type = 'default', banner = false, ...other } = props;
  const className = ...;

  return <SnackbarContent ref={ref} className={className} {...other} />
}

Đây là lỗi mà tôi nhận được khi tôi sử dụng thành phần SnackbarContent tùy chỉnh:

Thuộc tính 'css' bị thiếu trong kiểu '{ type: "error" | "default" | "success" | undefined; message: string; action: Element; }' nhưng lại bắt buộc trong kiểu 'Pick'.

Tôi không hiểu tại sao thuộc tính css lại là vấn đề ở đây vì chúng tôi không sử dụng emotion hay styled-component (chúng tôi đang sử dụng JSS).

Thật kỳ lạ, tất cả các loại đều được kiểm tra đúng khi tôi gỡ bỏ forwardRef.

Một cách khả thi để giảm bớt vấn đề này là thêm thuộc tính css vào MySnackbarContentProps và đặt nó thành một giá trị như: css?: null. Vì lý do nào đó, thuộc tính css là một thuộc tính bắt buộc. Không chắc chắn 100% tại sao lại như vậy ở đây. Tôi đã bỏ sót gì ở đây?

  • Phiên bản MUI: 4.9.10
  • Phiên bản TS: 3.6.2
  • React: 16.13.0
  • Các loại React: 16.9.34

EDIT: Giải thích nguyên nhân gốc rễ

Có ba phần liên quan khi cố gắng hiểu nguyên nhân gốc rễ.

Thứ nhất, Typescript cho phép mở rộng bất kỳ kiểu dữ liệu nào và kết quả của việc mở rộng sẽ là sự hợp nhất các thuộc tính trong các định nghĩa khác nhau. Ví dụ, đoạn mã dưới đây:

interface Person {
  name: string;
}

interface Person {
  age: number;
}

sẽ giống như viết câu sau:

interface Person {
  name: string;
  age: number
}

Sân chơi

Việc mở rộng này không cần phải nằm trong cùng một module/file; do đó, bất kỳ module nào trong dự án (bao gồm cả node_modules) có thể mở rộng bất kỳ kiểu dữ liệu nào trong module khác).

Thứ hai, Material UI cho phép truyền tất cả các thuộc tính DOM tới nhiều thành phần của nó. Vì vậy, về cơ bản nó mở rộng React.DOMAttributes<T> cơ bản (ví dụ Paper mở rộng React.DOMAttributes<HTMLDivElement>) trong nhiều thành phần.

Đối với tôi, tôi có thể tìm ra vấn đề vì tôi biết rằng css là một thuộc tính của emotion, được Storybook sử dụng; vì vậy, tôi đã tìm hiểu sâu hơn để xem emotion làm gì và đã tìm thấy những điều sau: https://github.com/emotion-js/emotion/blob/31e610f2385d5a3dfd532b31f743e5f6b9fee43b/packages/react/types/index.d.ts#L99

Vì vậy, emotion đang mở rộng React.DOMAttributes<T> bằng cách thêm thuộc tính tùy chọn css vào đó. Nếu bất kỳ kiểu nào kế thừa từ kiểu này, chúng sẽ có quyền truy cập vào thuộc tính css. Điều này đủ để tôi xác định thủ phạm. Thư viện đang xuất một tiện ích vốn được dùng trong Storybook (một số hàm của Storybook đã được nhập từ các tiện ích đó). Việc loại bỏ thành phần đó khỏi kết quả đầu ra đã giải quyết vấn đề. Nếu bạn đang sử dụng Storybook, hãy kiểm tra xem gói xuất ra có sử dụng bất kỳ thành phần nào của Storybook không.

Một điều mà tôi đã không điều tra là tại sao css lại là một thuộc tính bắt buộc mặc dù emotion đang truyền prop đó dưới dạng tùy chọn.

Câu trả lời #1

Cảm ơn bạn đã theo dõi, Gasim!

Đối với tôi, vấn đề gốc rễ cũng được gây ra bởi việc Storybook được đóng gói cùng với ứng dụng, vì chúng tôi đang sử dụng các tệp Storybook với phần mở rộng *.stories.tsx. Typescript tải tất cả các tệp .tsx trong thiết lập của chúng tôi, điều này lần lượt tải global Emotion prop như OP đã đề cập.

Tôi đã phải thêm "exclude": ["**/*.stories.tsx"] vào tsconfig.json của chúng tôi để sửa lỗi này.

Bài viết liên quan