-
Notifications
You must be signed in to change notification settings - Fork 168
Coding Standard
As always, we practice KISS and DRY within the project. In addition to that, we practice additional constraint to keep the code base consistent.
Above each class definition, it is required to have comments on what is the class purpose within the application. The only class exempted from class comments are self-explanatory class (short class, ~<50 LoC). Additionally, state its requirements/dependencies (if any). Below is an example of class comment.
/**
* This is an example of class comment.
* The class fetches the assessment of students.
*/
class SampleClass extends React.Component<...> { ... };
Above each high-complexity function definition, it is required to have comments on what is the purpose of the function. The only functions exempted from function comments are self-explanatory functions (short function, ~<10 LoC). Additionally, state its requirements/dependencies (if any). Below is an example of function comment.
/**
* This is an example of function comment.
* The function serves the purpose of: ...
*/
const sampleFunction = () => { ... };
Other than class and function comments, all other comments must use the single-line comments (\\
). Good code should be self-explanatory and not need much comments to begin with. Hence, if you find yourself writing many comments, consider refactoring the code for clarity.
We declare the attributes within a type State
outside the class, in which will be used as part of the React.Component.
type State = {
a: ... ;
b: ... ;
};
class Example extends React.Component<..., State> {
constructor() {
this.state = {
a = ...
b = ...
};
}
}
Within the class, the methods should be ordered as:
- Constructor
- Public methods
- Private methods
Within public or private methods, the methods should be ordered as:
- React component methods
- Handlers
- Helpers
Below is an example of order of methods.
class EgExample extends ... = () => {
constructor()
public componentDidMount()
public render()
private handlerEventA()
private handlerEventZ()
private helperMethodA()
private helperMethodZ()
}
Within this project, we restrict that all import paths should be of relative paths. This is to standardise with our testing that only accepts relative paths.
Imports should be divided into three groups:
// node-module imports
import * from 'react';
// js-slang imports
import { Variant } from 'js-slang/variant';
// local imports
import SampleContainer from '../features/sample/SampleContainer';
Between import statements, the import statements must be ordered alphabetically based on its import path. Within a group import, the modules must be sorted alphabetically.
import {
aaa,
bbb,
ccc,
sss
} from ...; <-- Modules are ordered alphabetically within group imports
import zComponent from './aaa/component'
import aComponent from './zzz/component' <-- Alphabetical order based on import path
export default
should be conserved when there is one main component within the file. Anything other than the main component should be exported normally.
Similarly, when there is only ONE main component within the file, please use export default
.
Unless absolutely needed, prefer Types to Interface. Interface should only be used when there is overriding of particular attributes. If it is a union of attributes, please use types instead.
All interface name must be prepended with the letter I
e.g. ISampleInterface
.
type ComponentProps = DispatchProps & OwnProps & StateProps;
type DispatchProps = { ... };
type OwnProps = { ... };
type StateProps = { ... };
interface IParent {
willBeOverriden: string | number | null;
sharedAttr: number | null;
}
interface IChild extends IParent {
willBeOverriden: string; <-- type is overriden, meaningful interface
}
Always export functions or modules under a unique name. We provide the following example:
const ExampleContainer = withRouter<IPropType>(
connect(
mapStateToProps,
mapDispatchToProps
)(Example)
);
export default ExampleContainer;
Similarly when importing the module, do not rename the module as much as possible. We have seen the horror of programmer naming ability, and do not wish to perpetuate it. The following is an example of what should NOT been done:
import IWantToBeSpecialSoIRenameTheComponent from './ExampleContainer'
If the interface or types are not used outside the module, please do not export it.
Within this project, index.ts
will only specifically refer to the root page i.e. src/index.ts
.
Other than this, we do NOT welcome usage of index.ts
in this project. While it has its uses under conventional practice, it has inflicted more confusion than benefits under this project.
For example, ISampleInterface
.
The three props must be defined at its component file. Do not rename it to anything else. The combination of the three props, must be named as ClassNameProps.
// In Workspace.tsx
type WorkspaceProps = DispatchProps & StateProps & OwnProps; // simple union
type DispatchProps = { ... };
type StateProps = { ... };
type OwnProps = { ... };
Prefer one line format:
const EgExample extends React.Component<DispatchProps, {}> = () => {
...
}
Compared to:
const EgExample extends React.Component<
DispatchProps, {}
> = () => {
...
}
All global constants must be stored at src/utils/constants.ts
, and stored under Constants
object. Thus, it will be accessed as constants.sampleGlobalVar
. The constants themselves must be named in camelCase convention.
The only exception to this is action string constants.
Action string constants must be of SHOUTY convention, e.g. FETCH_STUDENT_ID
.
Below is the proposed the following convention of file naming:
File | Name | Description |
---|---|---|
Component Parent
|
Parent.tsx | The component name is the file name. |
Container of Component Parent
|
ParentContainer.ts | The component name is appended with Container . |
Subcomponent of Parent : Child
|
ParentChild.tsx | The main component name must be prepended to the file name. |
All file names must be of PascalCase. In the case where the first word is an abbreviation e.g. MCQ Question Component, we still enforce PascalCase convention i.e. McqQuestion.tsx
.
Before merging a PR, at least ensure the followings are fulfilled:
-
Ensure that all modules and functions are exported under a UNIQUE name (except for DispatchProps, StateProps, and OwnProps). Do a simple global search of the name within the project to ensure that it is unique.
-
Import container as container, component as component i.e.
import AContainer from './AContainer'
but notimport A from './AContainer'
-
There is no single letter variable.