Обновленный ответ: после добавления типов пересечений через &
, можно «на лету» «слить» два предполагаемых типа.
Вот общий помощник, который считывает свойства некоторого объекта 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
вы получите ошибку типа.
NB Однако здесь есть еще одно типовое отверстие. 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 };
.