Zaktualizowana odpowiedź: od czasu dodania typów skrzyżowań poprzez &, możliwe jest „scalenie” dwóch wywnioskowanych typów w locie.
Oto ogólny pomocnik, który odczytuje właściwości jakiegoś obiektu fromi kopiuje je na obiekt onto. Zwraca ten sam obiekt, ontoale z nowym typem, który zawiera oba zestawy właściwości, więc poprawnie opisuje zachowanie w czasie wykonywania:
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 pomocnik niskiego poziomu nadal wykonuje asercję typu, ale zgodnie z projektem jest bezpieczny dla typu. Mając tego pomocnika, mamy operatora, którego możemy użyć do rozwiązania problemu OP z pełnym bezpieczeństwem 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 wypróbować to w TypeScript Playground . Zauważ, że ograniczyliśmy się foodo typu Foo, więc wynik mergemusi być kompletny Foo. Więc jeśli zmienisz nazwę barna bad, pojawi się błąd typu.
Uwaga: jest tu jednak jeszcze jeden rodzaj otworu. TypeScript nie zapewnia sposobu na ograniczenie parametru typu, aby nie był on funkcją. Więc możesz się pogubić i przekazać swoją funkcję jako drugi argument merge, a to nie zadziała. Więc dopóki nie zostanie to zadeklarowane, musimy to 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;
}
var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };.