Typescript函数重载不匹配

时间:2018-03-07 14:42:08

标签: typescript

我遇到函数重载问题。编译器继续选择错误的签名,我想了解原因。

这是一个显示行为的代码段: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'

有些事情我做错了。

2 个答案:

答案 0 :(得分:2)

问题是Typescript使用结构兼容性来确定类型兼容性。在函数签名解析的情况下,编译器将按定义顺序评估每个重载,以找到参数query可从参数分配的第一个重载。

由于QueryShowCollection没有成员,因此它将与所有其他Query*Collection类型在结构上兼容,这就是为所有集合获得show的原因。此外,QueryDescribeCollectionQueryCollectionExists在结构上完全相同,您可以按照区分两者的方式对重载进行排序。

你有两个解决方案

  • 将具有更复杂结构的重载命令排序到结构较不复杂的重载,以使编译器选择正确的overloed
  • 添加私有字段以确保类之间的类型不兼容。不必使用该字段,只需存在即可。

第一个解决方案对你不起作用,因为你的一些类在结构上是相同的,所以第二个解决方案看起来像这样:

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(''));