Typescript 编译时同类型约束

Typescript compile-time same-type constraint

这是一个简短的例子:

abstract class Base {
    private _id: number;

    protected set id(value: number) {
        this._id = value;
    }
    protected get id(): number {
        return this._id;
    }

}

abstract class Mid extends Base {
    equals(another: Mid) {
        if (this.constructor.name !== another.constructor.name) {
            throw TypeError(`Cannot compare ${this.constructor.name} to ${another.constructor.name}`);
        }
        return this.id === another.id;
    }
}

class ChildA extends Mid {
    constructor(public name: string) {
        super();
    }
}
class ChildB extends Mid {
    constructor(public name: string) {
        super();
    }
}

const a = new ChildA('Joe');
const b = new ChildB('John');

a.equals(b); // PREVENT!

我有什么

如果您尝试将 ChildAChildB 进行比较,它会抛出 TypeError.

我想要的

我想静态地防止在不同 类 之间使用 equals 方法。如果不允许我在源代码中比较苹果和橘子,那么我就不需要在 运行 时间抛出错误! (又少考写了)

请注意,Mid 会像 Entity 一样非常笼统,而 Child 类 会像客户、订单等。 - 具有商业意义的事物。将 Customers 与 Orders 进行比较是没有意义的,因此我想通过类型在源代码中展示这一点(毕竟,在我看来,这就是使用 TypeScript 的全部意义)。

问题

  1. 我怎样才能做到这一点?
  2. 我应该实现这个目标吗?也许我不需要担心开发人员(包括我自己)做傻事?

您可以限制参数派生自您正在访问的变量类型 equal 来自:

abstract class Mid extends Base {
    equals<T extends this>(another: T) {
        // ...
        return this.id === another.id;
    }
}

class ChildA extends Mid {
    private foo: string;
    constructor(public name: string) {
        super();
    }
}
class ChildAA extends ChildA {
    private foo2: string;
    constructor( name: string) {
        super(name);
    }
}
class ChildB extends Mid {
    private bar: string;
    constructor(public name: string) {
        super();
    }
}

const a = new ChildA('Joe');
const b = new ChildB('John');

a.equals(b); //Error
a.equals(new ChildAA(''));  // Works if you don't want this to work, use equals(another: this)  as the signature instead

由于 Typescript 使用结构类型,如果 ChildAChildB 具有相同的结构,上面的代码将不会阻止您调用 equals 但只要 ChildAChildB 在结构上不兼容,你会得到一个错误。

关于你问题的第二部分,你应该评估天气这会带来更多的悲伤十个好处,这似乎是一个很好的限制,但你应该看看它在实践中的效果。