Full Stack Open Part9

Last

Preparation

Type Definition for Third Party Libraries

  • In TypeScript, we want every variable to have a type defined in order to perform further static checks.

  • But in some cases, we would need third party libraries which are not written in TypeScript(therefore don’t contain types).

  • Under this circumstance, we could look for the community-maintained type packages, for example, express and cors.

    1
    2
    3
    yarn add express
    yarn add -D @types/express
    yarn add -D cors @types/cors

Eslint

  • To configure a project, for example an Express.js backend, we will need these packages installed:

    1
    yarn add -D eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser
  • Then, create a eslint.config.mjs as the config file for eslint:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    import typescriptEslint from "@typescript-eslint/eslint-plugin";
    import globals from "globals";
    import tsParser from "@typescript-eslint/parser";
    import path from "node:path";
    import { fileURLToPath } from "node:url";
    import js from "@eslint/js";
    import { FlatCompat } from "@eslint/eslintrc";

    const **filename = fileURLToPath(import.meta.url);
    const **dirname = path.dirname(**filename);
    const compat = new FlatCompat({
    baseDirectory: **dirname,
    recommendedConfig: js.configs.recommended,
    allConfig: js.configs.all,
    });

    export default [
    ...compat.extends(
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking",
    ),
    {
    plugins: {
    "@typescript-eslint": typescriptEslint,
    },
    languageOptions: {
    globals: {
    ...globals.browser,
    ...globals.node,
    },

    parser: tsParser,
    ecmaVersion: 5,
    sourceType: "commonjs",

    parserOptions: {
    project: "./tsconfig.json",
    },
    },
    rules: {
    "@typescript-eslint/semi": ["error"],
    "@typescript-eslint/explicit-function-return-type": "off",
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "@typescript-eslint/restrict-template-expressions": "off",
    "@typescript-eslint/restrict-plus-operands": "off",
    "@typescript-eslint/no-unsafe-member-access": "off",

    "@typescript-eslint/no-unused-vars": [
    "error",
    {
    argsIgnorePattern: "^_",
    },
    ],
    "no-case-declarations": "off",
    },
    },
    {
    ignores: ["eslint.config.mjs", "node_modules/", "build/"],
    },
    ];
  • For more information about configuration of Eslint above version 9.0, refer to Configure ESLint .

tsconfig.json

  • To set up some compile(transpile) options for the current project, we would need a tsconfig.json under the root directory.

  • We can use tsc to auto-generate one:

    • Firstly, install the native compiler of TypeScript tsc by:

      1
      yarn add -D typescript
    • Secondly, add related script in package.json:

      1
      2
      3
      4
      5
      6
      7
      8
      {
      ...,
      "scripts": {
      "tsc": "tsc",
      ...
      },
      ...
      }
    • Finally, run yarn tsc --init to generate the tsconfig.json file automatically.

  • As the course required, these compiler options should be written to tsconfig.json:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "compilerOptions": {
    "target": "ES6",
    "outDir": "./build/",
    "module": "commonjs",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true
    }
    }
  • For the auto-generated config file, we could just search for the related options and uncomment them.

CORS Issue

  • Discussed before in part3.b

  • If we want the resources returned by the backend to be accessed by a different origin or domain, we should install cors package and ‘use’ it in express:

    1
    yarn add -D cors @type/cors
    1
    2
    3
    4
    5
    import express from "express";
    import cors = require("cors");

    const app: Express = express();
    app.use(cors()); // this line is requried

Parse JSON HTTP Request Body

  • To accept data transmitted from the frontend in the http request body, we need to ‘use’ the json module of express:

    1
    2
    3
    4
    import express from "express";

    const app: Express = express();
    app.use(json()); // this line is requried

Node and JSON Modules

  • There’s a way to import JSON files as a module and assign ‘type’ to the content imported.

  • To do this, we will need to enable the resolveJsonModule option in tsconfig.json:

    1
    "resolveJsonModule": true /* Enable importing .json files. */,
  • By then, we can import data from JSON file just as we import it from a ts/js file:

    1
    2
    3
    import diaries from "../../data/entries";

    import { DiaryEntry } from "../types";
  • But be noted of a problem that may arise when using the tsconfig resolveJsonModule option:

    • By default, node will try to resolve modules in order of extensions:

      1
      ["js", "json", "node"]
    • By enabling resolveJsonModule, the list of possible node module extensions is extended to:

      1
      ["js", "json", "node", "ts", "tsx"]
    • Then, consider a flat folder structure containing files:

      1
      2
      ├── myModule.json
      └── myModule.ts
    • In TypeScript, with the resolveJsonModule option set to true, the file myModule.json becomes a valid node module. Now, imagine a scenario where we wish to take the file myModule.ts into use:

      1
      import myModule from "./myModule";
    • We notice that the .json file extension takes precedence over .ts(from the extended node module extensions). So, myModule.json will be imported and not myModule.ts.

  • To avoid time-eating bugs, it is recommended that within a flat directory, each file with a valid node module extension has a unique filename.

Path Aliases(Mappings)

  • We can setup path aliases in tsconfig.json file to avoid related pathes import or long import statement. For example:

    1
    2
    3
    4
    5
    6
    7
    8
    "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
    "paths": {
    "@/*": ["src/*"],
    "@data/*": ["data/*"],
    "@utils/*": ["src/utils/*"],
    "@services/*": ["src/services/*"],
    "@routers/*": ["src/routers/*"]
    } /* Specify a set of entries that re-map imports to additional lookup locations. */,

tsc & ts-node Ignoring Path Aliases

  • ts-node and ts-node-dev will ignore the path aliases declared in tsconfig.json.

Dev

  • My preferred workaround for ts-node-dev is to use the tsconfig-paths package:

    • install tsconfig-paths:

      1
      yarn add -D tsconfig-paths
    • then modify the command in package.json:

      1
      2
      3
      4
      5
      "scripts": {
      ...,
      "dev": "tsnd --respawn -r tsconfig-paths/register index.ts",
      ...
      }

Build

  • When it comes to the build stage, one more package needs to be added: @ef-carbon/tspm. For example:

    • install tsconfig-paths and @ef-carbon/tspm: …

    • then modify the command in package.json:

      1
      2
      3
      ...,
      "build": "tsc && ef-tspm",
      ...

yarn create

  • I want to run this npm command with yarn:

    1
    npm create vite@latest my-app-name -- --template react-ts
  • The identical commands for yarn are:

    1
    2
    yarn global add create-vite
    create-vite --template react-ts

To Be Continued…

  • Title: Full Stack Open Part9
  • Author: Last
  • Created at : 2024-07-08 17:52:12
  • Link: https://blog.imlast.top/2024/07/08/fullstackopen-p9/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments