-
Notifications
You must be signed in to change notification settings - Fork 5
Development Practices
Modularise functions. If your file or function is getting too big, break it up <3
Keep branches short and focused. If refactor work is needed, finish that off in a another branch first. Makes code reviews less painful, get things merged in faster.
Follow branch name convention:
<module>/#<ticket-number>/description
e.g. frontend/#69/adding-cool-box
No spaces - use dashes. No capitalisation.
Note we have components and view as separate folders. Components are for reusable components, view is for describing the actual page layout.
> api
> component
> types
> view
> Header
> Timetable
- Timetable.tsx
- App.tsx
- App.test.tsx
> utils
- colorMapper.ts
- colorMapper.test.ts
- index.ts
import React, { FunctionComponent } from 'react'
interface MyComponentProps {
name: string
meow(): void
meowName(name: string): string
} // always have object as react prop. Type using interfaces
const MyComponent: FunctionComponent<MyComponentProps> = ({ name, meow, meowName }) => { // destructure here
return <div />
}
export { MyComponent } // named exports and named imports
The view (jsx part) shouldn't contain logic. Keep them separate for readbility.
GOOD:
const MyComponent = () => {
const handleClick = () => {
console.log()
}
return <button onClick={handleClick} />
}
BAD:
const MyComponent = () => {
return <button onClick={() => console.log()} />
}
Use interface to define objects. Use types for everything else. Interfaces has extra functionality such as (extends) which helps with objects.
interfaces
GOOD:
interface TimePeriod {
start: Date
end: Date
}
BAD:
type TimePeriod = {
start: Date
end: Date
}
// types are good for more complex typescript
// type unions, type intersections, conditional, generic types
type ClassData = NormalClassData | SpecialClassData
Prefer non-arrow function signatures for interfaces (for syntax formatting & slightly nicer readibility)
GOOD:
interface MyInterface {
name: string
addNumbers(num1: number, num2: number): number
}
BAD:
interface MyInterface {
name: string
addNumbers: (num1: number, num2: number) => number
}
It's okay to not explicitly type variables/objects if it is obvious what type they are. If it's not obviously clear, then you should be typing them explicitly:
const cat = (): string | number => {...}
const dog: string | number = cat()
GOOD:
const foo = (): bar => {...}
BAD:
function foo() {...}
This is to enforce that the arguments you pass into a function have the exact same names. This is to prevent accidentally mis-ordering your arguments.
GOOD:
const foo = ({arg1, arg2, arg3}: {arg1: string, arg2: string, arg3: boolean}): string => {...}
or if you want it to be more readable
interface fooParams {
arg1: string,
arg2: string,
arg3: boolean
}
const foo = ({arg1, arg2, arg3}: fooParams): string => {...}
BAD:
const foo = (arg1: string, arg2: string, arg3: boolean): string => {...}
Aim for functional practices since its cleaner
use map
, filter
, some
, every
etc. over for
loops
_.chain
is neat, allows you to chain multiple lodash functions together
Aim for pure functions, no mutations and should only depend on parameters
If it gets overly complex with functional style, feel free to fall back to cleanest solution