typescript - Tại sao việc áp dụng các kiểu tổng quát như Omit<T, K> cho một hàm lại khiến nó không thể gọi được?
Tôi đang thử nghiệm với TypeScript và nhận thấy rằng khi áp dụng Omit<T, K> lên một hàm có thể gọi được, nó sẽ không còn có thể gọi được nữa:
declare function myCallableFunction(): void;
myCallableFunction(); // valid
type NonOmittedFunction = typeof myCallableFunction;
declare const myNonOmittedFunction: NonOmittedFunction;
myNonOmittedFunction(); // valid
type OmittedFunction = Omit<typeof myCallableFunction, 'foobar'>;
declare const myOmittedFunction: OmittedFunction;
myOmittedFunction(); // This expression is not callable.
// Type 'OmittedFunction' has no call signatures.
Tại sao lại như vậy?
Đây là một ví dụ được làm rườm rà về nơi bạn có thể muốn làm điều gì đó như thế này:
declare type CallCountingFunction = (() => void) & { count: number }
const myFunction: CallCountingFunction = (() => {
const x = () => {};
x.count = 0;
return x;
})()
myFunction.count; // valid
myFunction() // valid
type OmittedFunction = Omit<CallCountingFunction, 'count'>;
declare const myOmittedFunction: OmittedFunction;
myOmittedFunction(); // This expression is not callable.
// Type 'OmittedFunction' has no call signatures.
Sân chơi
Điều này dường như đúng cho tất cả các kiểu tiện ích tổng quát liên quan đến việc lập lại ánh xạ như Partial<T> và Required<T>.
Câu trả lời #1
Các kiểu tiện ích không phải là phép màu, chúng thực sự đều được viết bằng Typescript và chúng ta có thể xem chúng. Omit được định nghĩa dựa trên Pick và Exclude.
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Chưa có gì quá đáng, nhưng Pick được định nghĩa là
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Vì vậy Pick<T, K> luôn luôn được định nghĩa là một kiểu đối tượng với các khóa được lập chỉ mục. Không có chữ ký gọi nào ở đây. Thật dễ nghĩ đến Omit như là “lấy kiểu T của tôi và loại bỏ một số thứ cụ thể”, nhưng về mặt nội bộ nó thực sự là “tạo một kiểu đối tượng mới có các khóa của T, trừ một số thứ”, và vì khả năng gọi một hàm không phải là một khóa đối tượng, nó không được chuyển giao. Phép so sánh này không còn phù hợp vì các hàm, về cơ bản, không thực sự được thiết kế để sử dụng theo cách này.
