Jump to content

TypeScript

From Bath Wiki
Revision as of 07:00, 26 August 2026 by Pm2022 (talk | contribs) (cat)

Again, please in most cases use TypeScript over JavaScript.

Overview

This should mostly be done by biome or prettier and eslint, but so you know

  • Try to prioritise readability of code

  • 2 spaces as tabs

  • Lines should not be longer than 80 characters (install extension)

  • Trailing commas when the bracket on a new line

  • NO trailing whitespaces! (Install an extension to remove it for you)

  • Space after comments e.g. // something

  • Order imports alphabetically

  • Prefer documentation over comments (see below on how you do that)

  • Comments should only be written when necessary, mainly to describe why something is done the way it is or to example a really unreadable bit of code (however this normally means you should change the code).

  • Methods should be ordered alphabetically (with private methods ordered separately and at the bottom of the file)

    export function aFunc() {}
    export function bFunc() {}
    export function zFunc() {}
    function aPrivateFunc() {}
    function bPrivateFunc() {}
    

These are really strict, if you take anything away remember this:

  • Use a formatter and linter: styling should be consistent throughout the project
  • Readability of code takes precedent

Use a Linter

Linters usually will catch most issues, these can be installed as extensions to your preferred editor. E.g. for TypeScript we will be using biome or ESLint.

For Biome, both formatting and linting can be configured with biome.json:

{
  "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "files": {
    "includes": ["**", "!!**/dist"]
  },
  "formatter": {
    "enabled": true,
    "indentWidth": 2,
    "indentStyle": "space"
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double"
    }
  },
  "assist": {
    "enabled": true,
    "actions": {
      "source": {
        "organizeImports": "on"
      }
    }
  },
  "css": {
    "parser": {
      "tailwindDirectives": true
    }
  },
  "overrides": [
    {
      "includes": ["**/*.svelte", "**/*.astro", "**/*.vue"],
      "linter": {
        "rules": {
          "style": {
            "useConst": "off",
            "useImportType": "off"
          },
          "correctness": {
            "noUnusedVariables": "off",
            "noUnusedImports": "off"
          }
        }
      }
    }
  ]
}

Example ESLint config (.estlintrc.cjs) which supports, react and typescript:

You will want to customise this to your projects needs. But this is a good base
import js from "@eslint/js";
import prettier from "eslint-plugin-prettier";
import tsParser from "@typescript-eslint/parser";
import eslintJs from "@eslint/js";
import eslintReact from "@eslint-react/eslint-plugin";
import globals from "globals";
import ts from "@typescript-eslint/eslint-plugin";

export default [
  {
    files: ["**/*.{ts,tsx,mjs,js,jsx}", "astro.config.mjs"],
    languageOptions: {
      globals: { ...globals.browser },
      sourceType: "module",
      ecmaVersion: 2022,
      parser: tsParser,
      parserOptions: {
        project: ["./tsconfig.json"],
        ecmaVersion: "latest",
        sourceType: "module",
        ecmaFeatures: {
          jsx: true, // Enable JSX syntax support
        },
      },
    },
    plugins: {
      eslintJs: eslintJs.configs.recommended,
      eslintReact: eslintReact.configs.recommended,
      prettier: prettier,
      "@typescript-eslint": ts,
    },
    settings: {
      "mdx/code-blocks": true,
    },
    rules: {
      "@typescript-eslint/triple-slash-reference": "off",
    },
  },
  {
    ...js.configs.recommended,
    ...ts.configs.recommendedTypeChecked,
    files: ["**/*.ts", "**/*.tsx"],
    rules: {
      "@typescript-eslint/strict-boolean-expressions": [
        2,
        {
          allowString: false,
          allowNumber: false,
        },
      ],
    },
  },
];

Node that you must add the following dependencies:

@eslint/js
eslint-plugin-prettier
@typescript-eslint/parser
@eslint-react/eslint-plugin
@typescript-eslint/eslint-plugin

For formatters, we recommend prettier with the following config:

{
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": false,
  "quoteProps": "as-needed",
  "jsxSingleQuote": false,
  "bracketSpacing": true,
  "bracketSameLine": false,
  "arrowParens": "always",
  "printWidth": 80,
  "useTabs": false
}

(you will need to add prettier as a dependency).

It is also worth noting that people have started to mention moving towards biome instead of prettier (even the devs behind prettier), so you may want to use that instead

You can then add the following to your deno.jsonc:

{
  "tasks": {
    // Biome requires running /bin/sh which means it effectively wants all
    // permissions
    "biome": "deno run -A npm:@biomejs/biome",

    // ...
    "lint": "deno task biome lint",
    "lint:fix": "deno task biome lint --write",
    "format": "deno task biome check --write",
    "format:check": "deno task biome check",
    
    // With ESLINT + Prettier
    "eslint": "deno run -A npm:eslint",
    "prettier": "deno run -A npm:prettier",
    "prettier:files": "deno task prettier 'src/**/*.{tsx,ts,md,mdx,json}' '*.{json,js,mjs,md}' '.prettierrc'",

    "lint": "deno task eslint src --report-unused-disable-directives --max-warnings 0",
    "lint:fix": "deno task lint --fix",
    "format": "deno task prettier:files --write",
    "format:check": "deno task prettier:files --check"
  },
}

Specifically

These are normally the typical styling recommendations for JavaScript:

  • camelCase for variables + functions. Preferably, if you had an acronym in the name, all letters should be the same case e.g. myNPC or npcPair

  • PascalCase for classes

  • SCREAMING_CASE for global constants

  • Use const by default. Use let only if you need to reassign to the variable.

    Constants in JavaScript + TypeScript are not actually constant, they just can’t be reassigned.

  • Reduce use of any. Unfortunately TypeScript can be quite dumb at times so this can be used for type conversion.

  • Prefer " over ' for strings, unless you have " inside the string

    This one is more personal so not necessary (consistency though!)

  • Starting a new scope with { should be on the same line, not on a new line, e.g. in javascript:

    function something(my_arg) {
        ...
    }
    
    // NOT
    function something(my_arg)
    {
        ...
    }
    
  • else and else if statements should be on the same line as the scope (ignore the lecturers’ preferences for this).

    if (myVar === 'Something') {
      ...
    } else if (myOtherVar !== 'THING') {
      ...
    } else {
      ...
    }
    
    // NOT
    if (myVar === 'Something') {
      ...
    }
    else if (myOtherVar !== 'THING') {
      ...
    }
    else {
      ...
    }
    
  • Prefer creating your own interface over using the object keyword

  • ESLint basically covers all my other issues

Documentation

TypeScript and JavaScript have actually good documentation (probably the only good thing about them), so please use it.

Basically please read through this.

Package manager

As an asside, as already alluded to, BOSS has a standard of using deno for its package manage and runtime due to its significant focus on security. However, this comes with some learning barriers as it unlike your regular npm, yarn or bun and goes further than pnpm.

Our configuration of deno is as follows, which can be found in the deno.jsonc of any project:

{
  "$schema": "https://raw.githubusercontent.com/denoland/deno/refs/tags/v2.8.0/cli/schemas/config-file.v1.json",

  "tasks": {
    // ... Any "scripts" from package.json
  },

  "minimumDependencyAge": "P2D",
  "vendor": true,
  "lock": { "frozen": true }
}

What this configuration does is as follows:

  • When updating, the chosen dependency verion must be older than 2 days to help mitigate from supply chain attacks
  • By default, the lock file is frozen, and so when adding or updating packages, you must explicity include --frozen=false
  • vendor folder is used as a local cache for remote modules, decreasing overall disk usage.

Core differences with npm

  • Added packages must include a namespace, e.g. anything from npm (basically anything) should be prefixed with npm:. You can also install from the JSR by prefixing with jsr:
  • Scripts are not found in package.json instead they should be in deno's config: deno.jsonc under tasks
  • Running "tasks" should be done through deno's task cmd: deno task ...
  • Running package scripts, the full package name must be used e.g. deno run -A npm:@biomejs/biome
  • You must permit applications to use features such as environmental variables or reading your directories. If deploying yourself, it will ask for each permission. You can also permit anything by using deno run -A