打字稿:使用 class 作为界面
Typescript: Use class as interface
我尝试使用 Class 作为另一个 class 的界面。有了这个,我正在尝试完成一个最新的模拟Class 用于测试。
这应该是可行的,并且是一种有利的方法,如 https://angular.io/guide/styleguide#interfaces
中所述
并在此处演示:
但奇怪的是,我在 VSCode 1.17.2
中使用 Typescript 2.5.3 时遇到错误
class SpecialTest
错误
[ts]
Class 'SpecialTest' incorrectly implements interface 'Test'.
Types have separate declarations of a private property 'name'.
示例代码:
class Test {
private name: string;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
class SpecialTest implements Test {
private name: string;
getName(): string {
return '';
}
setName(name: string): void {
}
}
我错过了什么?
编辑:
使用 string
而不是 String
,正如@fenton 所建议的
在您链接到 的 post 中
你在实施中做了最后一部分吗:
It should be noticed that any class can be used as an interface in >TypeScript. So if there's no real need to differentiate between interface >and implementation, it may be just one concrete class:
export class Foo {
bar() { ... }
baz() { ... }
}
...
provider: [Foo]
...
Which may be used later as an interface if necessary:
export class SomeFoo implements Foo { ... }
...
provider: [{ provide: Foo, useClass: SomeFoo }]
...
在 中,父 class 应该是抽象的并充当接口。 TypeScript 接口没有访问修饰符,所有接口属性都应为 public.
当像 Test
这样的具体 class 用作接口时出现问题,因为实现无法实现私有属性,也无法覆盖它们。
Test
和 SpecialTest
都应该实现一些抽象 class 作为接口(在这种情况下,抽象 class 只能有 public 成员)或者从它继承(在这种情况下抽象 class 可以有 protected
个成员)。
在我们开始之前,当您扩展 class 时,您会使用 extends
关键字。您扩展了一个 class,并实现了一个接口。 post.
下方还有关于此的附加注释
class SpecialTest extends Test {
此外,请注意 string
与 String
,因为这会让您失望。您的类型注释几乎肯定应该是 string
(小写)。
最后,不需要手动给构造器参数赋值,所以原文:
class Test {
private name: string;
constructor(name: string) {
this.name = name;
}
// ...
}
更好地表示为:
class Test {
constructor(private name: string) {
}
// ...
}
现在您可以从多种解决方案中进行选择。
受保护的成员
保护 name
成员,然后你可以在 sub classes:
中使用它
class Test {
protected name: string;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
class SpecialTest extends Test {
getName(): string {
return '';
}
setName(name: string): void {
}
}
界面
这是我认为最符合您需求的解决方案。
如果您将 public 成员拉入一个接口,您应该能够将两个 class 都视为该接口(无论您是否显式使用 implements
关键字 - TypeScript 是结构类型的。
interface SomeTest {
getName(): string;
setName(name: string): void;
}
如果您愿意,可以明确实施它:
class SpecialTest implements SomeTest {
private name: string;
getName(): string {
return '';
}
setName(name: string): void {
}
}
您的代码现在可以依赖于接口而不是具体的 class。
使用 Class 作为接口
在技术上可以将 class 引用为接口,但是在使用 implements MyClass
执行此操作之前存在问题。
首先,您为以后必须阅读您的代码的任何人(包括未来的您)增加了不必要的复杂性。您还使用了一种模式,这意味着您需要注意关键字。意外使用 extends
可能会在将来更改继承的 class 时导致棘手的错误。维护者需要对使用哪个关键字保持鹰眼。一切为了什么?在结构语言中保留名义上的习惯。
界面是抽象的,不太可能改变。 Class这些更具体,也更有可能发生变化。使用 class 作为接口破坏了依赖稳定抽象的整个概念......反而导致你依赖不稳定的具体 classes.
考虑整个程序中 "classes as interfaces" 的扩散。对 class 的更改(假设我们添加了一个方法)可能会无意中导致变化在很远的距离内波动...现在程序的多少部分拒绝输入,因为输入不包含一个方法连用都没有?
更好的选择(当没有访问修饰符兼容性问题时)...
从 class:
创建接口
interface MyInterface extends MyClass {
}
或者,在您的第二个 class 中完全不引用原始 class。允许结构类型系统检查兼容性。
旁注...根据您的 TSLint 配置,弱类型(例如只有可选类型的接口)将触发 no-empty-interface
-Rule。
私有成员的具体案例
其中 None(使用 class 作为接口,从 class 或结构类型生成接口)解决了私有成员的问题。这就是为什么解决实际问题的解决方案是创建一个带有 public 成员的接口。
在private成员的具体情况下,比如问题中,我们想一想,如果按照原来的模式继续下去会怎么样?自然的结果是,为了保留使用 class 作为接口的模式,成员的可见性将被更改,如下所示:
class Test {
public name: string;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
现在我们正在打破更成熟的面向对象原则。
跟进@Fenton 在使用Class作为接口
中的解释
使用 InstanceType<T>
实用程序类型可以更明确地说明 class SpecialTest implements Test
。例如class SpecialTest implements InstanceType<typeof Test>
。我发现阅读代码时不那么混乱了。
在你自己的代码中更喜欢接口,这个魔法 我只在我需要为第 3 方创建装饰器时发现 useful/necessary class.
要从 class 中提取一个接口,它只包含 public 属性而不是 class 的所有实现细节,您可以使用 keyof
运算符与 Pick<Type, Keys>
实用程序一起使用。
class Test {
foo: any;
private bar: any;
}
class SepcialTest implements Pick<Test, keyof Test> {
foo: any;
}
Pick<Type, Keys>
Constructs a type by picking the set of properties Keys from Type.
[...] keyof T
, the index type query operator. For any type T, keyof T is the union of known, public property names of T.
我尝试使用 Class 作为另一个 class 的界面。有了这个,我正在尝试完成一个最新的模拟Class 用于测试。
这应该是可行的,并且是一种有利的方法,如 https://angular.io/guide/styleguide#interfaces
中所述并在此处演示:
但奇怪的是,我在 VSCode 1.17.2
中使用 Typescript 2.5.3 时遇到错误class SpecialTest
错误[ts]
Class 'SpecialTest' incorrectly implements interface 'Test'.
Types have separate declarations of a private property 'name'.
示例代码:
class Test {
private name: string;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
class SpecialTest implements Test {
private name: string;
getName(): string {
return '';
}
setName(name: string): void {
}
}
我错过了什么?
编辑:
使用 string
而不是 String
,正如@fenton 所建议的
在您链接到
It should be noticed that any class can be used as an interface in >TypeScript. So if there's no real need to differentiate between interface >and implementation, it may be just one concrete class:
export class Foo { bar() { ... } baz() { ... } } ... provider: [Foo] ...
Which may be used later as an interface if necessary:
export class SomeFoo implements Foo { ... } ... provider: [{ provide: Foo, useClass: SomeFoo }] ...
在
当像 Test
这样的具体 class 用作接口时出现问题,因为实现无法实现私有属性,也无法覆盖它们。
Test
和 SpecialTest
都应该实现一些抽象 class 作为接口(在这种情况下,抽象 class 只能有 public 成员)或者从它继承(在这种情况下抽象 class 可以有 protected
个成员)。
在我们开始之前,当您扩展 class 时,您会使用 extends
关键字。您扩展了一个 class,并实现了一个接口。 post.
class SpecialTest extends Test {
此外,请注意 string
与 String
,因为这会让您失望。您的类型注释几乎肯定应该是 string
(小写)。
最后,不需要手动给构造器参数赋值,所以原文:
class Test {
private name: string;
constructor(name: string) {
this.name = name;
}
// ...
}
更好地表示为:
class Test {
constructor(private name: string) {
}
// ...
}
现在您可以从多种解决方案中进行选择。
受保护的成员
保护 name
成员,然后你可以在 sub classes:
class Test {
protected name: string;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
class SpecialTest extends Test {
getName(): string {
return '';
}
setName(name: string): void {
}
}
界面
这是我认为最符合您需求的解决方案。
如果您将 public 成员拉入一个接口,您应该能够将两个 class 都视为该接口(无论您是否显式使用 implements
关键字 - TypeScript 是结构类型的。
interface SomeTest {
getName(): string;
setName(name: string): void;
}
如果您愿意,可以明确实施它:
class SpecialTest implements SomeTest {
private name: string;
getName(): string {
return '';
}
setName(name: string): void {
}
}
您的代码现在可以依赖于接口而不是具体的 class。
使用 Class 作为接口
在技术上可以将 class 引用为接口,但是在使用 implements MyClass
执行此操作之前存在问题。
首先,您为以后必须阅读您的代码的任何人(包括未来的您)增加了不必要的复杂性。您还使用了一种模式,这意味着您需要注意关键字。意外使用 extends
可能会在将来更改继承的 class 时导致棘手的错误。维护者需要对使用哪个关键字保持鹰眼。一切为了什么?在结构语言中保留名义上的习惯。
界面是抽象的,不太可能改变。 Class这些更具体,也更有可能发生变化。使用 class 作为接口破坏了依赖稳定抽象的整个概念......反而导致你依赖不稳定的具体 classes.
考虑整个程序中 "classes as interfaces" 的扩散。对 class 的更改(假设我们添加了一个方法)可能会无意中导致变化在很远的距离内波动...现在程序的多少部分拒绝输入,因为输入不包含一个方法连用都没有?
更好的选择(当没有访问修饰符兼容性问题时)...
从 class:
创建接口interface MyInterface extends MyClass {
}
或者,在您的第二个 class 中完全不引用原始 class。允许结构类型系统检查兼容性。
旁注...根据您的 TSLint 配置,弱类型(例如只有可选类型的接口)将触发 no-empty-interface
-Rule。
私有成员的具体案例
其中None(使用 class 作为接口,从 class 或结构类型生成接口)解决了私有成员的问题。这就是为什么解决实际问题的解决方案是创建一个带有 public 成员的接口。
在private成员的具体情况下,比如问题中,我们想一想,如果按照原来的模式继续下去会怎么样?自然的结果是,为了保留使用 class 作为接口的模式,成员的可见性将被更改,如下所示:
class Test {
public name: string;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
现在我们正在打破更成熟的面向对象原则。
跟进@Fenton 在使用Class作为接口
中的解释使用 InstanceType<T>
实用程序类型可以更明确地说明 class SpecialTest implements Test
。例如class SpecialTest implements InstanceType<typeof Test>
。我发现阅读代码时不那么混乱了。
在你自己的代码中更喜欢接口,这个魔法 我只在我需要为第 3 方创建装饰器时发现 useful/necessary class.
要从 class 中提取一个接口,它只包含 public 属性而不是 class 的所有实现细节,您可以使用 keyof
运算符与 Pick<Type, Keys>
实用程序一起使用。
class Test {
foo: any;
private bar: any;
}
class SepcialTest implements Pick<Test, keyof Test> {
foo: any;
}
Pick<Type, Keys>
Constructs a type by picking the set of properties Keys from Type.
[...]
keyof T
, the index type query operator. For any type T, keyof T is the union of known, public property names of T.