Обновленный ответ: после добавления типов пересечений через &
, можно «на лету» «объединить» два предполагаемых типа.
Вот общий помощник, который считывает свойства некоторого объекта 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 Playground. Обратите внимание, что мы ограничили 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;
}
person
Daniel Earwicker
schedule
10.03.2016
var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };
. - person Jonathan   schedule 29.11.2017