Обновленный ответ: поскольку добавление типов пересечений через &, можно «объединить» два выведенные типа на лету.
Вот общий помощник , который считывает свойства какого - либо объекта 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}`)
}
}
);
Нажмите здесь , чтобы попробовать его в машинописи Playground . Обратите внимание , что мы сдержали fooбыть типа Foo, поэтому результат mergeдолжен быть полным Foo. Так что если вы переименовать barв badто вы получите сообщение об ошибке типа.
NB Существует еще один тип отверстия здесь, однако. Машинопись не обеспечивает способ , чтобы ограничить параметр типа , чтобы быть «не является функцией». Таким образом , вы можете запутаться и передать вашу функцию в качестве второго аргумента 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;
}