在接口中定义时,字符串文字类型字段分配错误
String literal typed field assign error when defined in interface
当在接口中定义字符串文字类型时,出现意外行为。
interface IFoo {
value: 'foo' | 'boo';
}
当我在 class 中实现接口时出现错误:
class Foo implements IFoo {
value = 'foo';
}
我得到一个错误:类型 'Foo' 中的 属性 'value' 不能分配给基本类型 '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.value
属于 any
类型。我预计会收到 Boo 未实现 IFoo 的错误,但没有任何错误。
我发现唯一正确的方法是这样实现 classes:
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 类型。看起来在接口中声明字符串文字类型的字段是没有用的,或者我做错了什么。并且还发现 classes 可以为字段实现类型为 any 的接口,所以这取决于开发人员。
问题是您希望 implements IFoo
影响 class 字段的键入方式。它不是。事情发生的方式是 class 字段被键入,就好像 implements Foo
不存在一样,并且在 class 类型被完全解析之后,检查它与已实现接口的兼容性。这样看,错误是有道理的。
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';
}
当在接口中定义字符串文字类型时,出现意外行为。
interface IFoo {
value: 'foo' | 'boo';
}
当我在 class 中实现接口时出现错误:
class Foo implements IFoo {
value = 'foo';
}
我得到一个错误:类型 'Foo' 中的 属性 'value' 不能分配给基本类型 '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.value
属于 any
类型。我预计会收到 Boo 未实现 IFoo 的错误,但没有任何错误。
我发现唯一正确的方法是这样实现 classes:
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 类型。看起来在接口中声明字符串文字类型的字段是没有用的,或者我做错了什么。并且还发现 classes 可以为字段实现类型为 any 的接口,所以这取决于开发人员。
问题是您希望 implements IFoo
影响 class 字段的键入方式。它不是。事情发生的方式是 class 字段被键入,就好像 implements Foo
不存在一样,并且在 class 类型被完全解析之后,检查它与已实现接口的兼容性。这样看,错误是有道理的。
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';
}