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 from
i kopiuje je na obiekt onto
. Zwraca ten sam obiekt, onto
ale 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ę foo
do typu Foo
, więc wynik merge
musi być kompletny Foo
. Więc jeśli zmienisz nazwę bar
na 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 };
.