一份更新后的答案:自从通过&
添加了交叉类型,现在可以“合并”两个推断出的类型。
下面是一个通用的帮助函数,它读取某个对象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;
}
var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };
. - Jonathan