我阅读了TypeScript编码准则
但我发现这个声明有些令人困惑:
不要在接口名称前使用“I”作为前缀
我的意思是,没有“I”前缀,类似这样的东西就没有太多意义。
class Engine implements IEngine
我是否错过了什么显而易见的东西?
还有一件事情我不是很明白,就是这个:
类
为了保持一致性,在核心编译器管道中不要使用类,而应该使用函数闭包。
这是说我根本不应该使用类吗?
希望有人能为我澄清一下 :)
我阅读了TypeScript编码准则
但我发现这个声明有些令人困惑:
不要在接口名称前使用“I”作为前缀
我的意思是,没有“I”前缀,类似这样的东西就没有太多意义。
class Engine implements IEngine
我是否错过了什么显而易见的东西?
还有一件事情我不是很明白,就是这个:
类
为了保持一致性,在核心编译器管道中不要使用类,而应该使用函数闭包。
这是说我根本不应该使用类吗?
希望有人能为我澄清一下 :)
I
作为接口前缀的支持者认为,这种前缀有助于立即理解类型是否为接口。声称前缀有助于立即理解类型的说法是对 匈牙利命名法 的呼应。用 I
作为接口名称前缀,C
作为类前缀,A
作为抽象类前缀,s
作为字符串变量前缀,c
作为常量变量前缀,i
作为整数变量前缀。我认为这种命名修饰可以在不用将鼠标悬停在标识符上或通过热键导航到类型定义的情况下提供给您类型信息。然而,这种微小的好处被 匈牙利命名法 的缺点和下面提到的其他原因所抵消。匈牙利命名法在当代框架中不再使用。C# 由于历史原因(COM)使用 I
前缀(也是 C# 中唯一的前缀)表示接口。回想起来,.NET 架构师之一(Brad Abrams)认为 不使用 I
前缀 更好。TypeScript 不受 COM 遗留问题的影响,因此没有 I
前缀作为接口规则。
I
前缀违反了封装原则假设你得到了一个黑盒子。你获得了一些类型引用,允许你与该盒子进行交互。你不应该关心它是接口还是类。你只使用其接口部分。要求知道它是什么(接口、特定实现或抽象类)是违反封装的。
例如:假设你需要在代码中修复 API Design Myth: Interface as Contract ,例如删除 ICar
接口并改用 Car
基类。然后你需要在所有消费者中执行这样的替换。 I
前缀导致消费者对黑盒实现细节的隐式依赖。
I
即可获得接口名称。禁止在接口上使用I
前缀,迫使开发人员费尽心思选择适当的接口名称。所选名称不仅应在前缀上不同,而且还应强调意图差异。ICar
接口和相关的Car
类。 Car
是一个抽象概念,应该用于契约。实现应具有描述性、独特的名称,例如SportsCar、SuvCar、HollowCar
。WpfeServerAutosuggestManager实现AutosuggestManager
,FileBasedAutosuggestManager实现AutosuggestManager
。AutosuggestManager实现IAutosuggestManager
。
在我的实践中,我遇到了很多人,他们无思考地复制了类的接口部分到一个独立的接口中,采用 Car implements ICar
命名方案。将类的接口部分复制到独立的接口类型中,并不会自动将其转换为抽象。您仍会得到具体的实现,但也会带来重复的接口部分。如果您的抽象不够好,重复的接口部分不会任何改进。提取抽象是艰苦的工作。
注:在TS中,您不需要单独的接口来模拟类或重载功能。您可以使用TypeScript utility types而不是创建描述类的公共成员的单独接口。例如,Required<T>
构造一个由类型T
的所有公共成员组成的类型。
export class SecurityPrincipalStub implements Required<SecurityPrincipal> {
public isFeatureEnabled(entitlement: Entitlement): boolean {
return true;
}
public isWidgetEnabled(kind: string): boolean {
return true;
}
public areAdminToolsEnabled(): boolean {
return true;
}
}
关于您引用的链接的澄清:
这是有关TypeScript代码样式的文档,而不是实现项目的样式指南。
如果使用前缀对您和您的团队有意义,请使用它(我也是这么做的)。
如果不行,也可以使用Java风格的(接口),然后使用(实现),这样做也完全可以。
I
是有意义的。在一个 React 项目中,有一个名为 Card
的接口和一个名为 Card
的组件。在 props 中,我需要一个 Card
数组,但类型不是组件。我必须选择命名为 ICard
还是 CardComponent
... - BrunoLMSomeThingImpl 实现 SomeThing
和 SomeThing 实现 ISomeThing
一样好。如果 SomeThing
是抽象的话,命名应该是 Concrete[Some]Thing 实现 SomeThing
。 - Stanislav BerkovTImpl implements T
仍然比 X implements IX
更好,因为当我们懒惰时,我们谈论抽象而不知道所有实现细节及其后果,尽管我们认为我们正在谈论具体实现。ConcreteT implements T
稍微差一点,因为 ConcreteT
比 TImpl
更长。为了最大限度地简洁,只需使用 TI implements T
,因为实现也以字母 I
开头。 - Yufan Lou我认为@stanislav-berkov对于提问者的问题给出了相当不错的答案。我只想分享我的两分钱,最终达成共识并设立自己的规则/指南是由你的团队/部门/公司/等等来决定的。
尽可能和希望一致地坚持标准和约定是一个好习惯,也使事情更易于理解。另一方面,我认为我们仍然可以自由选择编写代码的方式。
在情感方面思考一下,我们编写代码的方式或我们的编码风格反映了我们的个性,在某些情况下甚至反映了我们的情绪。这就是让我们成为人类而不仅仅是遵循规则的编码机器的原因。我认为编码可以是一种工艺而不仅仅是工业化过程。
Typescript文档中建议的指南并不是给使用Typescript的人,而是给那些为Typescript项目做出贡献的人。如果您在页面开头阅读详细信息,它清楚地定义了谁应该使用该指南。这里是指南的链接。 Typescript guidelines
总之,作为开发人员,您可以根据自己的喜好命名接口。
interface FormProps {
some: string;
}
const Form:VFC<FormProps> = (props) => {
...
}
我个人非常喜欢通过添加-able后缀将名词变成形容词的想法。听起来很不正式,但我很喜欢!
interface Walletable {
inPocket:boolean
cash:number
}
export class Wallet implements Walletable {
//...
}
}
我正在尝试使用类似于其他答案的模式,但是将实例化具体类作为接口类型导出函数,如下所示:
export interface Engine {
rpm: number;
}
class EngineImpl implements Engine {
constructor() {
this.rpm = 0;
}
}
export const createEngine = (): Engine => new EngineImpl();
类型作为接口是实现细节。实现细节应该在API中隐藏。这就是为什么你应该避免使用 I
。
你应该避免使用前缀和后缀。以下两种都是错误的:
ICar
CarInterface
你应该做的是在API中显示一个漂亮的名称,并将实现细节隐藏在实现中。这就是为什么我建议:
Car
- 在API中公开的接口。CarImpl
- 该API的实现,对消费者隐藏。Impl
后缀会产生副作用,即用 Uncle Bob 替换那些因为 I
前缀而责骂你的自以为是的人。 - Szczepan HołyszewskiCartItem
的东西和一个 CartItem
组件,它需要一个 CartItem
作为 props
。你在这里建议的不可能,你需要一些后缀或前缀。 - Iniexport interface MyInterface {
createdDate : Date;
....
}
export class AuditableModel implements MyInterface{
createdDate : Date;
....
}
在你的基础设施包中,你可能会有:
export class AuditableDbEntity implements MyInterface {
@Property()
createdDate: Date = new Date();
...
}
那么,MyInterface 应该命名为什么呢? 在我看来,IAuditable 是一个不错的名称。我很难找到一个更好理解的名称。
这与在您的域中定义服务的情况不同:
export interface Logger {
log(msg:string)
}
在您的基础设施中,需要一个具体的实现,该实现将被注入到域中:
export class ConsoleLogger implements Logger {
log(msg:string){
console.log(msg)
}
}
let engine = new E...
,只会自动建议Classes而不是Interfaces。我之前没有意识到这一点 - 这就是我以'I'为前缀的原因之一。 - Drenai过了一段时间后,我意识到:接口的I前缀是一个缺陷(至少在TypeScript中),它会带来更多问题而不是好处。
- matronator