Typescript 与参数类型匹配的函数返回类型(使用扩展运算符时)?
我是否可以为下面的Typescript 与参数类型匹配的函数返回类型(使用扩展运算符时)?,typescript,typescript-typings,typescript-generics,Typescript,Typescript Typings,Typescript Generics,我是否可以为下面的Mixin()函数定义一个返回类型,该函数将解析为任何参数类型的交集类型 函数混合(…classRefs:any[]){ 返回合并(类{},…classRefs); } 函数合并(派生:any,…classRefs:any[]){ classRefs.forEach(classRef=>{ Object.getOwnPropertyNames(classRef.prototype).forEach(name=>{ 如果(名称!=“构造函数”){ Object.definePro
Mixin()
函数定义一个返回类型,该函数将解析为任何参数类型的交集类型
函数混合(…classRefs:any[]){
返回合并(类{},…classRefs);
}
函数合并(派生:any,…classRefs:any[]){
classRefs.forEach(classRef=>{
Object.getOwnPropertyNames(classRef.prototype).forEach(name=>{
如果(名称!=“构造函数”){
Object.defineProperty(
原型,
名称
Object.getOwnPropertyDescriptor(classRef.prototype,name)作为PropertyDescriptor
);
}
});
});
衍生收益;
}
福班{
foo(){}
}
分类栏{
bar(){}
}
Baz类{
baz(){
console.log('baz');
}
}
类MyClass扩展了Mixin(Foo,Bar,Baz){}
const my=new MyClass();
my.baz();
如果没有spread操作符,那么泛型就相当简单了。如何确保类型系统与实际发生的情况相匹配
编辑:我修改了示例以匹配我实际使用的mixin函数
编辑:两个后续问题:
…classRefs
中的所有类扩展一些公共基类?假设我创建了抽象类Mergable{}
,然后只有子类可以传递给merge
函数Mixin(…classRefs:[…R]):
新建(…args:any[])=>UnionToIntersection{
返回合并(类{},…classRefs);
}
函数合并(派生:ClassType,…classRefs:ClassType[]){
classRefs.forEach(classRef=>{
Object.getOwnPropertyNames(classRef.forEach)(名称=>{
常量描述符=Object.getOwnPropertyDescriptor(classRef,name);
如果(名称!='name'&&name!=='prototype'&&name!=='length'&&descriptor〕){
Object.defineProperty(
衍生的,
名称
描述符
)
}
});
//静态特性
Object.getOwnPropertyNames(classRef.forEach)(名称=>{
//你可以用这种方法摆脱类型转换
常量描述符=Object.getOwnPropertyDescriptor(classRef.prototype,名称)
如果(名称!='name'&&name!=='prototype'&&name!=='length'&&descriptor〕){
Object.defineProperty(
原型,
名称
描述符
);
}
});
//实例属性
Object.getOwnPropertyNames(classRef.prototype).forEach(name=>{
//你可以用这种方法摆脱类型转换
常量描述符=Object.getOwnPropertyDescriptor(classRef.prototype,名称)
if(名称!='constructor'&&descriptor){
Object.defineProperty(
原型,
名称
描述符
);
}
});
});
衍生收益;
}
您可以编写一个类型函数,该函数接受一个元组,并生成其所有元素的类型。显而易见的方法是:
本质上,此函数位于第一个构造函数的实例类型T
中;和U
,来自其余构造函数的实例类型的元组。因此,derived
有一个new()=>T
,而classRefs
有一个构造签名元组,表示为,从中获取U
的元素并用new()=>…
将其包装起来。返回类型是new()=>(T&IntersectAll)
,一个生成所有其他构造函数的交集的构造函数
而Mixin()
也同样通用:
function Mixin<T extends any[]>(
...classRefs: { [I in keyof T]: new () => T[I] }
) {
return merge<unknown, T>(class { }, ...classRefs);
}
/* function Mixin<T extends any[]>(
...classRefs: { [I in keyof T]: new () => T[I]; }
): new () => IntersectAll<T> */
好了。我假设这里有一些边缘案例,因为手动处理原型可以做一些有趣的事情。如果将具有冲突类型的类与相同属性或方法名的类混合在一起,则交集并不完全正确。当然,还有类构造函数参数。这样的边缘情况可能是可寻址的,具有额外的复杂性。。。但我认为这些都超出了范围。
请让我知道它是否适合您:
//积分归@jcalz
类型UnionToIntersection=
(U扩展任何?(k:U)=>void:never)扩展(
k:我推断
)=>无效
? 我
:从不;
类型推断=
T扩展推断R
? R扩展了new(…args:any)=>any
? 实例类型
:从不
:从不;
type ClassType=new(…args:any[])=>any;
函数Mixin(…classRefs:[…R]):
新建(…args:any[])=>UnionToIntersection{
返回合并(类{},…classRefs);
}
函数合并(派生:ClassType,…classRefs:ClassType[]){
classRefs.forEach(classRef=>{
Object.getOwnPropertyNames(classRef.prototype).forEach(name=>{
//你可以用这种方法摆脱类型转换
常量描述符=Object.getOwnPropertyDescriptor(classRef.prototype,名称)
if(名称!='constructor'&&descriptor){
Object.defineProperty(
原型,
名称
描述符
);
}
});
});
衍生收益;
}
福班{
foo(){}
}
分类栏{
bar(){}
}
Baz类{
baz(){
console.log('baz');
}
}
类MyClass扩展了Mixin(Foo,Bar,Baz){}
const my=new MyClass();
my.foo()//好的
my.bar()//好的
my.baz();//好啊
推断类型
类型推断=/*1*/T扩展推断R/*2*/R扩展新(…参数:任意)=>任意/*3*/实例类型:从不:从不;
// obvious version:
type IntersectAll<T extends readonly any[]> =
T extends [infer F, ...infer R] ? F & IntersectAll<R> : unknown;
// less obvious version:
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends
((k: infer I) => void) ? I : never
type IntersectAll<T extends readonly any[]> = Extract<
UnionToIntersection<
{ [K in keyof T]: [T[K]] }[number]
>, readonly any[]>[number]
function merge<T, U extends any[]>(
derived: new () => T,
...classRefs: { [I in keyof U]: new () => U[I] }
) {
classRefs.forEach(classRef => {
Object.getOwnPropertyNames(classRef.prototype).forEach(name => {
if (name !== 'constructor') {
Object.defineProperty(
derived.prototype,
name,
Object.getOwnPropertyDescriptor(
classRef.prototype, name) as PropertyDescriptor
);
}
});
});
return derived as new () => (T & IntersectAll<U>);
}
function Mixin<T extends any[]>(
...classRefs: { [I in keyof T]: new () => T[I] }
) {
return merge<unknown, T>(class { }, ...classRefs);
}
/* function Mixin<T extends any[]>(
...classRefs: { [I in keyof T]: new () => T[I]; }
): new () => IntersectAll<T> */
You can verify that it works as expected:
class Foo { foo() { return 6; } }
class Bar { bar() { return "abc" } }
class Baz { baz() { console.log('baz'); } }
class MyClass extends Mixin(Foo, Bar, Baz) { }
const my = new MyClass();
console.log(my.foo().toFixed(2)); // "6.00"
console.log(my.bar().toUpperCase()); // "ABC"
my.baz(); // "baz"