Một câu trả lời được cập nhật: kể từ khi bổ sung các loại nút giao thông qua &
, có thể "hợp nhất" hai loại suy luận một cách nhanh chóng.
Đây là một trình trợ giúp chung đọc các thuộc tính của một số đối tượng from
và sao chép chúng qua một đối tượng onto
. Nó trả về cùng một đối tượng onto
nhưng với một kiểu mới bao gồm cả hai bộ thuộc tính, do đó, mô tả chính xác hành vi thời gian chạy:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
Trình trợ giúp cấp thấp này vẫn thực hiện xác nhận kiểu, nhưng nó là kiểu an toàn theo thiết kế. Với trình trợ giúp này, chúng tôi có một toán tử mà chúng tôi có thể sử dụng để giải quyết vấn đề của OP với độ an toàn đầy đủ:
interface Foo {
(message: string): void;
bar(count: number): void;
}
const foo: Foo = merge(
(message: string) => console.log(`message is ${message}`), {
bar(count: number) {
console.log(`bar was passed ${count}`)
}
}
);
Nhấp vào đây để dùng thử trong TypeScript Playground . Lưu ý rằng chúng tôi đã hạn chế foo
là loại Foo
, vì vậy kết quả củamerge
phải hoàn chỉnh Foo
. Vì vậy, nếu bạn đổi tên bar
thành bad
thì bạn sẽ gặp lỗi kiểu.
NB Tuy nhiên, vẫn còn một loại lỗ ở đây. TypeScript không cung cấp cách hạn chế tham số kiểu là "không phải là một hàm". Vì vậy, bạn có thể nhầm lẫn và chuyển hàm của mình làm đối số thứ hai merge
, và điều đó sẽ không hoạt động. Vì vậy, cho đến khi điều này có thể được khai báo, chúng ta phải bắt nó trong thời gian chạy:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
if (typeof from !== "object" || from instanceof Array) {
throw new Error("merge: 'from' must be an ordinary object");
}
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };
.