Оновлена відповідь: з часу додавання типів перетину через &
, можна "об'єднати" два виведені типи на льоту.
Ось загальний помічник, який читає властивості якогось об’єкта from
та копіює їх над об’єктом onto
. Він повертає той самий об'єкт, onto
але з новим типом, що включає обидва набори властивостей, тому правильно описує поведінку середовища виконання:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
Цей помічник низького рівня все ще виконує твердження типу, але за типом конструкції він безпечний для типу. З цим помічником на місці ми маємо оператора, який ми можемо використовувати для вирішення проблеми OP з повною безпекою типу:
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}`)
}
}
);
Клацніть тут, щоб спробувати на майданчику TypeScript . Зверніть увагу, що ми обмежувались foo
типом Foo
, тому результат merge
повинен бути повним Foo
. Отже, якщо ви перейменуєте bar
наbad
то ви отримаєте помилку типу.
Примітка. Однак тут є ще одна діра типу. TypeScript не надає способу обмежити параметр типу "не функцією". Таким чином, ви можете заплутатися і передати свою функцію як другий аргумент merge
, і це не буде працювати. Отже, поки це не може бути оголошено, ми повинні ловити це під час виконання:
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 };
.