Skip to content

Commit 778ad37

Browse files
jelbournmhevery
authored andcommitted
docs: create coding standards doc (angular#37700)
Create initial document for Angular framework coding standards. This document will evolve over time. This version contains all non-controversial rules as discussed in a recent team meeting. Some text and examples were copied from angular/components. PR Close angular#37700
1 parent 5a73362 commit 778ad37

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

.pullapprove.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ groups:
10901090
'dev-infra/**',
10911091
'docs/BAZEL.md',
10921092
'docs/CARETAKER.md',
1093+
'docs/CODING_STANDARDS.md',
10931094
'docs/COMMITTER.md',
10941095
'docs/DEBUG.md',
10951096
'docs/DEBUG_COMPONENTS_REPO_IVY.md',

docs/CODING_STANDARDS.md

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Angular Framework Coding Standards
2+
3+
The coding practices in this doc apply only to development on Angular itself, not applications
4+
built _with_ Angular. (Though you can follow them too if you really want).
5+
6+
## Code style
7+
8+
The [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html) is the
9+
basis for Angular's coding style, with additional guidance here pertaining to TypeScript. The team
10+
uses `clang-format` to automatically format code; automatic formatting is enforced by CI.
11+
12+
## Code practices
13+
14+
### Write useful comments
15+
16+
Comments that explain what some block of code does are nice; they can tell you something in less
17+
time than it would take to follow through the code itself.
18+
19+
Comments that explain why some block of code exists at all, or does something the way it does,
20+
are _invaluable_. The "why" is difficult, or sometimes impossible, to track down without seeking out
21+
the original author. When collaborators are in the same room, this hurts productivity.
22+
When collaborators are in different timezones, this can be devastating to productivity.
23+
24+
For example, this is a not-very-useful comment:
25+
```typescript
26+
// Set default tabindex.
27+
if (!attributes['tabindex']) {
28+
element.setAttribute('tabindex', '-1');
29+
}
30+
```
31+
32+
While this is much more useful:
33+
```typescript
34+
// Unless the user specifies otherwise, the calendar should not be a tab stop.
35+
// This prevents ngAria from overzealously adding a tabindex to anything with an ng-model.
36+
if (!attributes['tabindex']) {
37+
element.setAttribute('tabindex', '-1');
38+
}
39+
```
40+
41+
In TypeScript code, use JsDoc-style comments for descriptions (on classes, members, etc.) and
42+
use `//` style comments for everything else (explanations, background info, etc.).
43+
44+
### API Design
45+
46+
#### Boolean arguments
47+
48+
Generally avoid adding boolean arguments to a method in cases where that argument means
49+
"do something extra". In these cases, prefer breaking the behavior up into different functions.
50+
51+
```typescript
52+
// AVOID
53+
function getTargetElement(createIfNotFound = false) {
54+
// ...
55+
}
56+
```
57+
58+
```typescript
59+
// PREFER
60+
function getExistingTargetElement() {
61+
// ...
62+
}
63+
64+
function createTargetElement() {
65+
// ...
66+
}
67+
```
68+
69+
You can ignore this guidance when necessary for performance reasons in framework code.
70+
71+
#### Optional arguments
72+
73+
Use optional function arguments only when such an argument makes sense for an API or when required
74+
for performance. Don't use optional arguments merely for convenience in implementation.
75+
76+
### TypeScript
77+
78+
#### Typing
79+
80+
Avoid `any` where possible. If you find yourself using `any`, consider whether a generic or
81+
`unknown` may be appropriate in your case.
82+
83+
#### Getters and Setters
84+
85+
Getters and setters introduce openings for side-effects, add more complexity for code readers,
86+
and generate additional code when targeting older browsers.
87+
88+
* Only use getters and setters for `@Input` properties or when otherwise required for API
89+
compatibility.
90+
* Avoid long or complex getters and setters. If the logic of an accessor would take more than
91+
three lines, introduce a new method to contain the logic.
92+
* A getter should immediately precede its corresponding setter.
93+
* Decorators such as `@Input` should be applied to the getter and not the setter.
94+
* Always use a `readonly` property instead of a getter (with no setter) when possible.
95+
96+
```typescript
97+
/** YES */
98+
readonly active: boolean;
99+
100+
/** NO */
101+
get active(): boolean {
102+
// Using a getter solely to make the property read-only.
103+
return this._active;
104+
}
105+
```
106+
107+
#### Iteration
108+
109+
Prefer `for` or `for of` to `Array.prototype.forEach`. The `forEach` API makes debugging harder
110+
and may increase overhead from unnecessary function invocations (though modern browsers do usually
111+
optimize this well).
112+
113+
#### JsDoc comments
114+
115+
All public APIs must have user-facing comments. These are extracted for API documentation and shown
116+
in IDEs.
117+
118+
Private and internal APIs should have JsDoc when they are not obvious. Ultimately it is the purview
119+
of the code reviewer as to what is "obvious", but the rule of thumb is that *most* classes,
120+
properties, and methods should have a JsDoc description.
121+
122+
Properties should have a concise description of what the property means:
123+
```typescript
124+
/** The label position relative to the checkbox. Defaults to 'after' */
125+
@Input() labelPosition: 'before' | 'after' = 'after';
126+
```
127+
128+
Methods blocks should describe what the function does and provide a description for each parameter
129+
and the return value:
130+
```typescript
131+
/**
132+
* Opens a modal dialog containing the given component.
133+
* @param component Type of the component to load into the dialog.
134+
* @param config Dialog configuration options.
135+
* @returns Reference to the newly-opened dialog.
136+
*/
137+
open<T>(component: ComponentType<T>, config?: MatDialogConfig): MatDialogRef<T> { ... }
138+
```
139+
140+
Boolean properties and return values should use "Whether..." as opposed to "True if...":
141+
```ts
142+
/** Whether the button is disabled. */
143+
disabled: boolean = false;
144+
```
145+
146+
#### Try-Catch
147+
148+
Only use `try-catch` blocks when dealing with legitimately unexpected errors. Don't use `try` to
149+
avoid checking for expected error conditions such as null dereference or out-of-bound array access.
150+
151+
Each `try-catch` block **must** include a comment that explains the
152+
specific error being caught and why it cannot be prevented.
153+
154+
##### Variable declarations
155+
156+
Prefer `const` wherever possible, only using `let` when a value must change. Avoid `var` unless
157+
absolutely necessary.
158+
159+
##### `readonly`
160+
161+
Use `readonly` members wherever possible.
162+
163+
#### Naming
164+
165+
##### General
166+
167+
* Prefer writing out words instead of using abbreviations.
168+
* Prefer *exact* names over short names (within reason). For example, `labelPosition` is better than
169+
`align` because the former much more exactly communicates what the property means.
170+
* Except for `@Input()` properties, use `is` and `has` prefixes for boolean properties / methods.
171+
172+
##### Observables
173+
174+
Don't suffix observables with `$`.
175+
176+
##### Classes
177+
178+
Name classes based on their responsibility. Names should capture what the code *does*,
179+
not how it is used:
180+
```typescript
181+
/** NO: */
182+
class DefaultRouteReuseStrategy { }
183+
184+
/** YES: */
185+
class NonStoringRouteReuseStrategy { }
186+
```
187+
188+
##### Methods
189+
190+
The name of a method should capture the action performed *by* that method rather than
191+
describing when the method will be called. For example:
192+
193+
```typescript
194+
/** AVOID: does not describe what the function does. */
195+
handleClick() {
196+
// ...
197+
}
198+
199+
/** PREFER: describes the action performed by the function. */
200+
activateRipple() {
201+
// ...
202+
}
203+
```
204+
205+
##### Test classes and examples
206+
207+
Give test classes and examples meaningful, descriptive names.
208+
209+
```ts
210+
/** PREFER: describes the scenario under test. */
211+
class FormGroupWithCheckboxAndRadios { /* ... */ }
212+
class InputWithNgModel { /* ... */ }
213+
214+
215+
/** AVOID: does not fully describe the scenario under test. */
216+
class Comp { /* ... */ }
217+
class InputComp { /* ... */ }
218+
```
219+
220+
#### RxJS
221+
222+
When importing the `of` function to create an `Observable` from a value, alias the imported
223+
function as `observableOf`.
224+
225+
```typescript
226+
import {of as observableOf} from 'rxjs';
227+
```
228+
229+
#### Testing
230+
231+
##### Test names
232+
233+
Use descriptive names for jasmine tests. Ideally, test names should read as a sentence, often of
234+
the form "it should...".
235+
236+
```typescript
237+
/** PREFER: describes the scenario under test. */
238+
describe('Router', () => {
239+
describe('with the default route reuse strategy', () => {
240+
it('should not reuse routes upon location change', () => {
241+
// ...
242+
});
243+
})
244+
});
245+
246+
/** AVOID: does not fully describe the scenario under test. */
247+
describe('Router', () => {
248+
describe('default strategy', () => {
249+
it('should work', () => {
250+
// ...
251+
});
252+
})
253+
});
254+
```

0 commit comments

Comments
 (0)