1

考虑以下 Typescript 类:

abstract class EnforcedString
{
    private minLength: number;
    private maxLength: number;
    private value: string;
    protected identifier: string;

    constructor(minLength: number, maxLength: number, identifier: string)
    {
        this.minLength = minLength;
        this.maxLength = maxLength;
        this.identifier = identifier;
    }

    public setValue(value: string): void
    {
        if (typeof value !== 'string') {
            throw(new Error(this.identifier + ' must be a valid string'));
        }        

        if (value.length > this.maxLength) {
            throw new Error(this.identifier + ' must have a maximum length of: ' + this.maxLength);
        }

        if (this.minLength > 0) {
            if (value.length < this.minLength) {
            const characterString = (this.minLength === 1) ? 'character' : 'characters';
            throw new Error(this.identifier + ' must have at least ' + this.minLength + ' ' + characterString);
        }
    }

        this.value = value;
    }

    public getValue(): string
    {
        return this.value;
    }

    public isEmpty(): boolean
    {
        return this.value.length === 0;
    }
}

class String255 extends EnforcedString
{
    constructor(value: string, identifier: string)
    {        
        super(0, 255, identifier);
        this.setValue(value);
    }
}

class String255Required extends EnforcedString
{   
    constructor(value: string, identifier: string)
    {        
        super(1, 255, identifier);
        this.setValue(value);
    }
}

由于 Typescript 的结构子类型系统,这意味着即使我们将 String255 类分配给 String255Required 属性,下面的构造函数中的“firstName”也不会报告为错误。

class User 
{
    private id: UserId;
    private username: Username;
    private firstName: String255Required;
    private lastName: String255Required;

    constructor(
        id: UserId,
        username: Username,
        firstName: String255,
        lastName: String255Required
    ) {
        this.id = id;
        this.username = username;
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

所以问题是,处理结构子类型的最佳实践是什么。我可以在“修复”问题的类中引入无用的属性,例如

class String255Required extends EnforcedString
{   
    private nope: boolean;      

    constructor(value: string, identifier: string)
    {        
        super(1, 255, identifier);
        this.setValue(value);
    }
}

编译器现在报告我所期望的错误,因为 String255Required 具有与 String255 不同的属性,但这显然是不可取的。人们在做什么来避免这些问题?请注意,我正在尝试使用类来执行基于类型的业务规则。

4

1 回答 1

0

我不认为你的目标是无论如何可以实现的。往下看:

class A { }

interface Test { }

class B extends A { }

class C {

    b1: B;

    constructor(b2: B) { }

}

const c1 = new C(new A);
const c2 = new C([]);
const c3 = new C(<Test>{});

c1.b1 = new A();
c2.b1 = [];
c3.b1 = <Test>{};

该代码是有效的,因为谁扩展谁并不重要。类的参数/实例具有什么属性很重要。

仅当参数没有所需的属性时才会引发错误。

有关详细信息,请参阅http://www.typescriptlang.org/docs/handbook/type-compatibility.html 。

于 2018-01-20T12:36:50.350 回答