我遇到函数重载问题。编译器继续选择错误的签名,我想了解原因。
这是一个显示行为的代码段:Typescript Playground。作为参考,这里是片段源:
class Query { }
class QueryInsert extends Query {
constructor(public readonly objects?: any[], public readonly collection?: any) { super(); }
}
class QueryUpdate extends Query {
constructor(public readonly object?: any, public readonly collection?: any, public readonly conditions?: any) { super(); }
}
class QueryShowCollection extends Query { }
class QueryCollectionExists extends Query {
constructor(public readonly collection: any) { super(); }
}
class QueryDescribeCollection extends Query {
constructor(public readonly collection: any) { super(); }
}
class QueryCreateCollection extends Query {
constructor(public readonly collection: any, public readonly columns?: any[], public readonly indexes?: any[]) { super(); }
}
class QueryDropCollection extends Query {
constructor(public readonly collection: any) { super(); }
}
function execute(query: QueryInsert): 'insert'
function execute(query: QueryUpdate): 'update'
function execute(query: QueryShowCollection): 'show'
function execute(query: QueryCollectionExists): 'exists'
function execute(query: QueryDescribeCollection): 'describe'
function execute(query: QueryCreateCollection): 'create'
function execute(query: QueryDropCollection): 'drop'
function execute(query: Query): any {
}
const insert = execute(new QueryInsert());
const update = execute(new QueryUpdate());
const show = execute(new QueryShowCollection());
const exists = execute(new QueryCollectionExists(''));
const describe = execute(new QueryDescribeCollection(''));
const create = execute(new QueryCreateCollection(''));
const drop = execute(new QueryDropCollection(''));
意外行为来自最后的常量。 insert
应报告"insert"
,update
,"update"
等。
在show
之后,每个常量报告"show"
。我理解编译器从function execute(query: QueryShowCollection): 'show'
中选择这些常量的签名。如果我们向下移动这个签名(例如:在具有QueryDropCollection的签名下面),那么现在为其余的常量采用function execute(query: QueryCollectionExists): 'exists'
。
有些事情我做错了。
答案 0 :(得分:2)
问题是Typescript使用结构兼容性来确定类型兼容性。在函数签名解析的情况下,编译器将按定义顺序评估每个重载,以找到参数query
可从参数分配的第一个重载。
由于QueryShowCollection
没有成员,因此它将与所有其他Query*Collection
类型在结构上兼容,这就是为所有集合获得show
的原因。此外,QueryDescribeCollection
和QueryCollectionExists
在结构上完全相同,您可以按照区分两者的方式对重载进行排序。
你有两个解决方案
第一个解决方案对你不起作用,因为你的一些类在结构上是相同的,所以第二个解决方案看起来像这样:
class Query { }
class QueryInsert extends Query {
private type: 'insert';
constructor(public readonly objects?: any[], public readonly collection?: any) { super(); }
}
class QueryUpdate extends Query {
private type: 'update';
constructor(public readonly object?: any, public readonly collection?: any, public readonly conditions?: any) { super(); }
}
class QueryShowCollection extends Query {
private type: 'show';
}
class QueryCollectionExists extends Query {
private type: 'exists';
constructor(public readonly collection: any) { super(); }
}
class QueryDescribeCollection extends Query {
private type: 'describe';
constructor(public readonly collection: any) { super(); }
}
class QueryCreateCollection extends Query {
private type: 'create';
constructor(public readonly collection: any, public readonly columns?: any[], public readonly indexes?: any[]) { super(); }
}
class QueryDropCollection extends Query {
private type: 'drop';
constructor(public readonly collection: any) { super(); }
}
function execute(query: QueryInsert): 'insert'
function execute(query: QueryUpdate): 'update'
function execute(query: QueryDescribeCollection): 'describe'
function execute(query: QueryCreateCollection): 'create'
function execute(query: QueryDropCollection): 'drop'
function execute(query: QueryShowCollection): 'show'
function execute(query: QueryCollectionExists): 'exists'
function execute(query: Query): any {
}
const insert = execute(new QueryInsert());
const update = execute(new QueryUpdate());
const show = execute(new QueryShowCollection());
const exists = execute(new QueryCollectionExists(''));
const describe = execute(new QueryDescribeCollection(''));
const create = execute(new QueryCreateCollection(''));
const drop = execute(new QueryDropCollection(''));
答案 1 :(得分:0)
我建议使用泛型而不是重载。因为你需要返回一个依赖于类的字符串常量,所以将常量作为一个字段是有意义的。
class Query {
readonly type: string;
}
class QueryInsert extends Query {
readonly type = 'insert';
constructor(public readonly objects?: any[], public readonly collection?: any) { super(); }
}
class QueryUpdate extends Query {
readonly type = 'update';
constructor(public readonly object?: any, public readonly collection?: any, public readonly conditions?: any) { super(); }
}
class QueryShowCollection extends Query {
readonly type = 'show';
}
class QueryCollectionExists extends Query {
readonly type = 'exists';
constructor(public readonly collection: any) { super(); }
}
class QueryDescribeCollection extends Query {
readonly type = 'describe';
constructor(public readonly collection: any) { super(); }
}
class QueryCreateCollection extends Query {
readonly type = 'create';
constructor(public readonly collection: any, public readonly columns?: any[], public readonly indexes?: any[]) { super(); }
}
class QueryDropCollection extends Query {
readonly type = 'drop';
constructor(public readonly collection: any) { super(); }
}
function execute<T extends Query>(query: T): T['type'] {
return query.type;
}
const insert = execute(new QueryInsert());
const update = execute(new QueryUpdate());
const show = execute(new QueryShowCollection());
const exists = execute(new QueryCollectionExists(''));
const describe = execute(new QueryDescribeCollection(''));
const create = execute(new QueryCreateCollection(''));
const drop = execute(new QueryDropCollection(''));