Uaktualniona odpowiedź: ponieważ dodanie typów przecięcia poprzez &możliwe jest, aby „połączyć” dwa przypuszczalne typy na bieżąco.
Oto ogólny pomocnik, który odczytuje właściwości jakiegoś przedmiotu fromi kopiuje je na obiekcie onto. Zwraca ten sam obiekt onto, ale z nowego typu, który zawiera oba zestawy właściwości, więc prawidłowo opisujące zachowanie środowiska wykonawczego:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
Ten niski poziom pomocnik ma jeszcze przeprowadzić typu twierdzenie, ale jest bezpieczny typu projektem. Z tego pomocnika w miejscu, mamy operatora, który możemy wykorzystać w celu rozwiązania problemu OP z pełną bezpieczeństwa typu:
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}`)
}
}
);
Kliknij tutaj, aby go wypróbować w maszynopisie Playground . Należy pamiętać, że mamy ograniczone foodo być typu Foo, więc wynik mergemusi być kompletny Foo. Więc jeśli przemianować barna badnastępnie pojawi się błąd typu.
NB Jest jeszcze jeden rodzaj dziura tu jednak. Maszynopis nie przewiduje sposobu Aby ograniczyć parametr typu być „nie jest funkcją”. Więc można się pomylić i przekazać swoją funkcję jako drugi argument merge, a to nie będzie działać. Więc dopóki ta może być uznana, musimy go złapać w czasie wykonywania:
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;
}