在接口中定义字符串文字类型字段时分配错误

时间:2018-09-19 07:15:47

标签: typescript typescript-types

当在接口中定义字符串文字类型时,出现意外行为。

interface IFoo {
    value: 'foo' | 'boo';
}

在类中实现接口时出现错误:

class Foo implements IFoo {
    value = 'foo';
}

我收到一个错误:无法将类型“ Foo”中的属性“值”分配给基本类型“ IFoo”中的相同属性。但是'foo'是字符串文字的正确值。

另一方面:

class Boo implements IFoo {
    value;
    constructor() {
        this.value = 'foo';
        this.value = 'boo';
        this.value = 'koo'; // must be an error Boo doesn't implement IFoo
    }
}
const test = new Boo();
test.value = 'koo';

此代码不会引起任何错误,但是Boo.valueany类型的。我预计会收到一个错误消息,说Boo没有实现IFoo,但是没有任何错误。

我发现的唯一正确方法是通过这种方式实现类:

class Koo implements IFoo {
    value: 'foo' | 'boo' = 'foo';
}

所以我不得不声明枚举:

enum Doos { foo = 'foo', boo = 'boo' }
interface IDoo {
    value: Doos;
}
class Doo implements IDoo {
    value = Doos.foo;
}
const test = new Doo();
test.value = Doos.boo;

我理解这是因为ts编译器从字段声明中的分配值中获得了Doo.value类型。在接口中声明字符串文字类型的字段看起来没用,或者我做错了。并且还发现,类可以为字段使用任何类型的接口,因此由开发人员来决定。

1 个答案:

答案 0 :(得分:1)

问题是您希望implements IFoo影响类字段的键入方式。它不是。发生的方式是键入类字段,就好像implements Foo不存在一样,并且在类类型已完全解析之后,将检查它与已实现的接口的兼容性。以这种方式看,错误是有意义的。

class Foo implements IFoo {
    value = 'foo'; // this is typed as string, not as the string literal type and thus is not compatible with value in IFoo 
}
class Boo implements IFoo {
    // no type, no init, value is typed as any and any is compatible with 'foo' | 'boo' 
    // use -noImplicitAny to avoid such errors
    value; 
    constructor() {
        this.value = 'foo';
        this.value = 'boo';
        this.value = 'koo'; // 'koo' is compatible with any
    }
}

使用枚举时,事情就起作用了,因为如果我们将枚举的值分配给字段,则该字段将被键入为枚举。

您可以指定value字段的类型,无论是显式还是相对于IFoo界面:

class Foo implements IFoo {
    value: IFoo['value'] = 'foo';
}

或者如果该字段是readonly,它将被键入为字符串文字类型:

class Foo implements IFoo {
    readonly value = 'foo';
}