Typescript lấy loại loại trừ tất cả các hàm từ một lớp
Tôi sao chép dữ liệu từ một thể hiện lớp bằng cách sử dụng toán tử spread: const myObj:MyType={...myClassInstance}, trong đó myClassInstance là một thể hiện của MyClass. Tôi muốn định nghĩa kiểu MyType, mà loại trừ tất cả các hàm được định nghĩa trên MyClass, chỉ giữ lại các chuỗi, số, mảng v.v. Tôi đã thử điều này:
type ExcludeSubType<Base, Condition> = Pick<Base, {
[Key in keyof Base]: Base[Key] extends Condition ? never : Key
}[keyof Base]>;
export type MyType = ExcludeSubType<MyClass, (_: any) => any>;
Lớp MyClass có thể trông giống như sau:
export class MyClass {
foo: string;
constructor(foo: string) {
this.foo = foo;
}
stopTimer() {
// stuff
}
startTimer() {
// stuff
}
continue(p1: string, p2: number) {
// stuff
}
setFailed(p1: string, p2: string) {
// more stuff
}
}
Nhưng MyType vẫn có các hàm trên nó - đặc biệt là các hàm có đối số. Tôi đang làm gì sai?
Câu trả lời #1
export type MyType = ExcludeSubType<MyClass, Function>;Kiểu (_: any) => any chỉ khớp với các hàm nhận tối đa một đối số.
Câu trả lời #2
Titian đã cung cấp câu trả lời, nhưng tôi muốn thử đưa ra một giải thích chi tiết hơn.
Vấn đề là bạn chỉ đang loại trừ đúng loại hàm rất cụ thể - cụ thể là các hàm nhận tối đa một đối số.
Quan hệ extends có thể được hiểu như sau: nếu A extends B, thì A có thể gán cho B. Trong trường hợp của bạn, điều này có nghĩa là các thành viên bị loại trừ phải tất cả có thể gán cho (_: any) => any, tức là bạn có thể truyền chúng đến một nơi mà ở đó các hàm với một đối số được mong đợi.
Nhưng, nếu hàm của bạn nhận hai đối số (như setFailed trong ví dụ của bạn), thì bạn không thể truyền nó vào nơi mà mong đợi hàm với một đối số. Nếu không, ai đó có thể gọi hàm của bạn mà không cung cấp tất cả những gì nó đang mong đợi. Vì vậy, (typeof setFailed) extends (_: any) => any là sai - hàm này sẽ không bị loại trừ, như bạn đã ghi chú.
Vậy, làm thế nào để thực hiện điều này? Rõ ràng, bạn nên loại trừ tất cả các hàm, bất kể chúng có bao nhiêu đối số. Điều đó có nghĩa là bạn cần một loại mà mọi loại hàm khác có thể được gán cho nó. Và chúng ta có hai lựa chọn cho điều này, được liệt kê trong các câu trả lời khác:
- Bạn có thể đơn giản sử dụng kiểu
Functiontừ thư viện chuẩn. Điều này sẽ hoạt động, nhưng kiểu này có thể bị một số công cụ kiểm tra mã cấm vì không rõ ràng. - Bạn có thể sử dụng kiểu rõ ràng. Chúng ta cần diễn đạt thực tế rằng hàm (giả tưởng) mà chúng ta đang mô tả có thể nhận số lượng đối số tùy ý lớn. Đó chính xác là trường hợp khi chúng ta có thể sử dụng tham số còn lại, bằng cách "thu thập" tất cả các đối số vào tuple:
(...args: any[]) => any.