打字稿:重载类级联构造函数

Typescript: Overloading cascade-like constructors

我需要将这些 Java 构造函数重载转换为 Typescript:

public QueryMixin() {
    this(null, new DefaultQueryMetadata(), true);
}

public QueryMixin(QueryMetadata metadata) {
    this(null, metadata, true);
}

public QueryMixin(QueryMetadata metadata, boolean expandAnyPaths) {
    this(null, metadata, expandAnyPaths);
}

public QueryMixin(T self) {
    this(self, new DefaultQueryMetadata(), true);
}

public QueryMixin(T self, QueryMetadata metadata) {
    this(self, metadata, true);
}

public QueryMixin(T self, QueryMetadata metadata, boolean expandAnyPaths) {
    this.self = self;
    this.metadata = metadata;
    this.expandAnyPaths = expandAnyPaths;
}

我已经尝试创建这些构造函数并查看了那里,但我一直无法弄清楚如何获得它...

有什么想法吗?

constructor();
constructor(metadata: QueryMetadata);
constructor(metadata: QueryMetadata, expandAnyPaths: boolean);
constructor(self: T);
constructor(self: T, metadata: QueryMetadata);
constructor(???) {
    this.self = self;  <<< ???
    this.metadata = selfOrMetadata;  <<< ???
    this.expandAnyPaths = expandAnyPaths;
}

在打字稿中,您可以为 method/constructor 指定多个签名,但只能指定一个实现。由实现来确定调用了哪个重载。

在您的情况下,它可能看起来像这样:

constructor();
constructor(metadata: QueryMetadata);
constructor(metadata: QueryMetadata, expandAnyPaths: boolean);
constructor(self: T);
constructor(self: T, metadata: QueryMetadata);
constructor() {
    let self: T = null;
    let metadata : QueryMetadata = null;
    let expandAnyPaths = true;
    if(arguments.length > 0) {
        // Asuming QueryMetadata is a class
        if(arguments[0] instanceof QueryMetadata) { 
            expandAnyPaths = arguments[1] || true;
            metadata = arguments[0];
        }
        else {
            self = arguments[0]
            metadata = arguments[1];
        }
    }

    this.self = self;
    this.metadata = metadata || new DefaultQueryMetadata();;
    this.expandAnyPaths = expandAnyPaths;
}

注意:实现签名对外界是隐藏的(即在寻找合适的构造函数时不考虑解析)。这就是为什么我们有两次空构造函数签名,一次用于 public 消费,一次作为实现签名。

看来您真正想要的是支持具有简单默认值的现有或缺失参数的任意组合。 3个参数,有8种on/off组合。所以虽然 TypeScript 支持重载,但是为了类型安全写出 8 个重载并不理想。

但是使用命名参数(而不是位置参数)将简化实现。可以轻松添加更多参数,而无需编写成倍增加的重载。

interface QueryMetadata { }
class DefaultQueryMetadata implements QueryMetadata { }

interface QueryMixinParams<T> {
    self: T;
    metadata: QueryMetadata;
    expandAnyPaths: boolean;
}

class QueryMixin<T> implements QueryMixinParams<T> {
    self: T;
    metadata: QueryMetadata;
    expandAnyPaths: boolean;

    constructor({
        self = null,
        metadata = new DefaultQueryMetadata(),
        expandAnyPaths = true,
        }: Partial<QueryMixinParams<T>> = {}) {
        this.self = self;
        this.metadata = metadata;
        this.expandAnyPaths = expandAnyPaths;
        console.log(this);
    }
}

// Any combination of parameters is supported
new QueryMixin();
new QueryMixin({});
new QueryMixin({ self: {} });
new QueryMixin({ self: {}, metadata: {} });
new QueryMixin({ self: {}, metadata: {}, expandAnyPaths: false });
new QueryMixin({ self: {}, expandAnyPaths: false });
new QueryMixin({ metadata: {} });
new QueryMixin({ metadata: {}, expandAnyPaths: false });
new QueryMixin({ expandAnyPaths: false });

Try it in TypeScript Playground