Code Constructs

This is the Construct. It's our loading program. Below is a non exhaustive list of general code costruct rules.

  • Prefer async/await with try/catch
  • Use arrow functions
  • Exit early instead of huge if tests
  • No nested ternary
  • Use two spaces as code indent
  • 120 characters printwidth
  • Use let and const, not var
  • Use single quotes
  • Use semicolons
  • Always add a trailing comma
  • Switch statements require a default case

General construct rules#

Prefer async/await#

Prefer to use async/await with try/catch instead of promises chaining. This is common syntax used in a lot of different languages, compared to Promoses which are special syntax in JavaScript. This is optional since it sometimes makes more sense to use promises chaining.

Good
try {
const foo = await getFoo();
console.log(foo);
} catch (error) {
console.error(error);
}
Bad
getFoo()
.then((foo) => {
console.log(foo);
})
.catch((error) => {
console.error(error);
});

Use arrow functions#

Good
const foo = (foo: Foo) => console.log('I am foo');
Bad
function foo(foo: Foo) console.log('I am foo')

Exit early instead of huge if tests#

Good
const foo = (foo: Foo) => {
if (!foo) return;
// lots of code
};
Bad
const foo = (foo: Foo) => {
if (foo) {
// lots of code
}
};

Pay attention to your imports and exports#

The JavaScript module syntax is very good and every developer should know how to use it. Please pay attention to your impoerts and exports. Always strive to write readable and consistent code.

Using named exports.

Good
import Home from './Home.svg';
import MyHealth from './MyHealth.svg';
export { Home, MyHealth };
// Usage
import { Home, MyHealth } from 'assets/icons';

Using export aggregating.

Good
import { default as Home } from './Home.svg';
import { default as MyHealth } from './MyHealth.svg';
// Usage
import { Home, MyHealth } from 'assets/icons';
Bad
import HomeSVG from './Home.svg';
export const Home = HomeSVG;
import MyHealthSVG from './Home.svg';
export const Home = MyHealthSVG;

Pass entities, not props#

REST API`s return complete entities. Entities should be passed further down the component tree, with their interfaces, instead of just passing parts of the enitity. This also applies to regular functions as well.

Good
interface FooViewerProps {
foo: Foo;
}
const FooViewer = (props: FooViewerProps) => {
return <div>{props.foo.fooProp}</div>;
};
Bad
interface FooViewerProps {
fooProps: string;
}
const FooViewer = (props: FooViewerProps) => {
return <div>{props.fooProp}</div>;
};

Always add a trailing comma#

Always adding a trailing comma makes refactoring a lot easier. Below is an example when declaring an object.

Good
const foo: Foo = {
bar: 'bar',
baz: 'baz',
};
Bad
// prettier-ignore
const foo: Foo = {
bar: 'bar',
baz: 'baz' // notice missing comma
};

Order of dependant methods#

When muliple methods inside a single file depend on each other, then their order of dependency should be reflected in the code.

A structured method order will#

  • Allow the reader to quickly identify the main method or component of a file.
  • Allow the reader to look through the code without jumping up and down unnecessarily.

Function guidelines#

  • The dependency first used by some method should be placed directly above it's dependant. The second method used should be placed above the first, and so on.
  • If a dependency itself has dependencies, then it's sub-dependencies should be placed directly above it in the order of usage.
  • Methods with several dependents should be placed above the last method that depends on it.

When these guidelines are followed, the main method or compoent of the file will always be placed at the bottom of the file, making it easy to locate. Methods with a higher level of abstraction will "sink to the bottom", with lower level methods "floating" above their parents.

Good
const subBoth = () => {};
const subSecond = () => {};
const second = () => {
subSecond();
subBoth();
};
const subFirst = () => {};
const first = () => {
subFirst();
subBoth();
};
export const main = () => {
first();
second();
};
Bad
const second = () => {
subSecond();
subBoth();
};
const subFirst = () => {};
const subSecond = () => {};
export const main = () => {
first();
second();
};
const first = () => {
subFirst();
subBoth();
};
const subBoth = () => {};

Enums#

Use PascalCase when declaring an enum. Both enums keys and values use uppercase. Prefer to use enums instead of object as constants when writing code in TypeScript.

Good
enum DisplayInterval {
HOUR = 'HOUR',
DAY = 'DAY',
WEEK = 'WEEK',
}
Bad
enum displayInterval {
hour = 'hour',
day = 'day',
week = 'week',
}

Variant mapping#

A common pattern is to use objects when mapping a type to a variant object.

Bad
enum UserType {
DOCTOR = 'DOCTOR',
PATIENT = 'PATIENT',
}
const professionCardVariant: UserType = {
[UserType.DOCTOR]: {
icon: <DoctorIcon />,
description: 'MD',
},
[UserType.PATIENT]: {
icon: <PatientIcon />,
description: 'Patient',
},
};
export const ProfessionCard = ({ userType }: { userType: UserType }) => {
return (
<Card startAdornemnt={variant[userType].icon}>
{variant[userType].description}
</Card>
);
};

At a glance, the above code looks like a normal approach when implementing variant handling. The problem with this code is that TypeScript is unaware of variant misses, e.g. when a new type is added by the server. Consider this, a new user type, NURSE, is added to the UserType enum. TypeScript does not require that all types are handled by the variant map, and in this case returns undefined when the UserType.NURSE key is dynamically accessed.
The component handling the mapping is unaware of the added type and when rendered can lead to a run time error if not properly handled.

A simple soulution is to enforce the use of a switch statement with required default case instead of dynamic object access. This way, we always return a value, even if it's undefined or something valid. The component using the variant map knows that the value might be undefined, forcing proper handling of variant misses.

Good
enum UserType {
DOCTOR = 'DOCTOR',
PATIENT = 'PATIENT',
}
const professionCardVariant = (type: UserType) => {
switch (type) {
case UserType.DOCTOR:
return {
icon: <DoctorIcon />,
description: 'MD',
};
case UserType.PATIENT:
return {
icon: <PatientIcon />,
description: 'Patient',
};
default:
return;
}
};
export const ProfessionCard = ({ userType }: { userType: UserType }) => {
const variant = professionCardVariant(userType);
if (!variant) return null;
return <Card startAdornemnt={variant.icon}>{variant.description}</Card>;
};

Next

Component formatting

me