26 |
27 | );
28 | }
29 | ```
30 |
31 | A new page is now available at `http://localhost:3000/my-react-page`.
32 |
33 | ## Create your first Markdown Page
34 |
35 | Create a file at `src/pages/my-markdown-page.md`:
36 |
37 | ```mdx title="src/pages/my-markdown-page.md"
38 | # My Markdown page
39 |
40 | This is a Markdown page
41 | ```
42 |
43 | A new page is now available at `http://localhost:3000/my-markdown-page`.
44 |
--------------------------------------------------------------------------------
/github-page/src/css/custom.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Any CSS included here will be global. The classic template
3 | * bundles Infima by default. Infima is a CSS framework designed to
4 | * work well for content-centric websites.
5 | */
6 |
7 | /* You can override the default Infima variables here. */
8 | :root {
9 | --ifm-color-primary: #2e8555;
10 | --ifm-color-primary-dark: #29784c;
11 | --ifm-color-primary-darker: #277148;
12 | --ifm-color-primary-darkest: #205d3b;
13 | --ifm-color-primary-light: #33925d;
14 | --ifm-color-primary-lighter: #359962;
15 | --ifm-color-primary-lightest: #3cad6e;
16 | --ifm-code-font-size: 95%;
17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
18 | }
19 |
20 | /* For readability concerns, you should choose a lighter palette in dark mode. */
21 | [data-theme='dark'] {
22 | --ifm-color-primary: #25c2a0;
23 | --ifm-color-primary-dark: #21af90;
24 | --ifm-color-primary-darker: #1fa588;
25 | --ifm-color-primary-darkest: #1a8870;
26 | --ifm-color-primary-light: #29d5b0;
27 | --ifm-color-primary-lighter: #32d8b4;
28 | --ifm-color-primary-lightest: #4fddbf;
29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
30 | }
31 |
--------------------------------------------------------------------------------
/github-page/docs/guidelines/project/package_name.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: package_name
3 | title: "Naming your package"
4 | authors: [unional]
5 | tags: [project]
6 | ---
7 |
8 | When you create a package,
9 | you always have to go through the painfull process of naming your package.
10 |
11 | While it is not specific to TypeScript,
12 | it is still beneficial to follow certain guidelines to make your life a bit easier.
13 |
14 | ---
15 |
16 | You **should** use `snake_case` for your package name.
17 |
18 | > Why?
19 |
20 | [NodeJS] is not opinionated about package names, but
21 | [Deno] prohibits using `kebab-case` or `PascalCase` as module name.
22 |
23 | Since [Deno] is likely to stay, you should use `snake_case` so that they are consistent.
24 |
25 | ---
26 |
27 | You **should** name your package with nouns.
28 |
29 | > Why?
30 |
31 | Naming your package with verbs typically means your package is doing just one thing.
32 |
33 | Of course, if that is what you want, that's fine.
34 |
35 | But naming your package with nouns allows you to add similar features to your package, without causing confusion.
36 |
37 | [Deno]: https://deno.land/x?page=2#Q&A
38 | [NodeJS]: https://nodejs.org/
39 |
--------------------------------------------------------------------------------
/apps/starlight/src/content/docs/guidelines/project/package-name.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: package_name
3 | title: "Naming your package"
4 | authors: [unional]
5 | tags: [project]
6 | ---
7 |
8 | When you create a package,
9 | you always have to go through the painful process of naming your package.
10 |
11 | While it is not specific to TypeScript,
12 | it is still beneficial to follow certain guidelines to make your life a bit easier.
13 |
14 | ---
15 |
16 | You **should** use `snake_case` for your package name.
17 |
18 | > Why?
19 |
20 | [Node.js] is not opinionated about package names, but
21 | [Deno] prohibits using `kebab-case` or `PascalCase` as module name.
22 |
23 | Since [Deno] is likely to stay, you should use `snake_case` so that they are consistent.
24 |
25 | ---
26 |
27 | You **should** name your package with nouns.
28 |
29 | > Why?
30 |
31 | Naming your package with verbs typically means your package is doing just one thing.
32 |
33 | Of course, if that is what you want, that's fine.
34 |
35 | But naming your package with nouns allows you to add similar features to your package, without causing confusion.
36 |
37 | [Deno]: https://deno.land/x?page=2#Q&A
38 | [NodeJS]: https://nodejs.org/
39 |
--------------------------------------------------------------------------------
/apps/starlight/src/content/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: TypeScript Blackbook
3 | description: How to use TypeScript efficiently
4 | template: splash
5 | hero:
6 | tagline: How to use TypeScript efficiently
7 | image:
8 | file: ../../assets/logo.svg
9 | actions:
10 | - text: Start reading
11 | link: /typescript-blackbook/guides/welcome/
12 | icon: right-arrow
13 | - text: Check out the blogs
14 | link: /typescript-blackbook/blogs/
15 | icon: right-arrow
16 | variant: minimal
17 | ---
18 |
19 | import { Card, CardGrid } from '@astrojs/starlight/components';
20 |
21 | ## Next steps
22 |
23 |
24 |
25 | Edit `src/content/docs/index.mdx` to see this page change.
26 |
27 |
28 | Add Markdown or MDX files to `src/content/docs` to create new pages.
29 |
30 |
31 | Edit your `sidebar` and other config in `astro.config.mjs`.
32 |
33 |
34 | Learn more in [the Starlight Docs](https://starlight.astro.build/).
35 |
36 |
37 |
--------------------------------------------------------------------------------
/.cursor/rules/templates/cursor_rules.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description: Template for creating new cursor rule files in TypeScript Blackbook
3 | globs:
4 | alwaysApply: false
5 | ---
6 |
7 | # Rule Title
8 |
9 | Brief introduction explaining the rule's purpose and when to apply it.
10 |
11 | ## Required Rules
12 |
13 | The AI agent should read and follow these rules along with the subject rule:
14 |
15 | - **[Required Rule 1](mdc:.cursor/rules/category/required_rule_1.mdc)**: Brief explanation of why this rule is required
16 | - **[Required Rule 2](mdc:.cursor/rules/category/required_rule_2.mdc)**: Brief explanation of why this rule is required
17 |
18 | ## Quick Reference
19 |
20 | [Include a summary table or bullet points for quick lookup]
21 |
22 | ## Table of Contents
23 |
24 | - [Section 1](#section-1)
25 | - [Section 2](#section-2)
26 |
27 | ## Section 1
28 |
29 | Detailed explanation with step-by-step instructions.
30 |
31 | ### ❌ Bad Examples to avoid
32 |
33 | [Include examples of what to avoid]
34 |
35 | ### ✅ Good Examples to follow
36 |
37 | [Include positive examples]
38 |
39 | ## Section 2
40 |
41 | Additional content organized in logical sections.
42 |
43 | ## Quality Checklist
44 |
45 | [Include verification steps to ensure compliance]
46 |
--------------------------------------------------------------------------------
/github-page/docusaurus-example/docs/tutorial-basics/create-a-document.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Create a Document
6 |
7 | Documents are **groups of pages** connected through:
8 |
9 | - a **sidebar**
10 | - **previous/next navigation**
11 | - **versioning**
12 |
13 | ## Create your first Doc
14 |
15 | Create a markdown file at `docs/hello.md`:
16 |
17 | ```md title="docs/hello.md"
18 | # Hello
19 |
20 | This is my **first Docusaurus document**!
21 | ```
22 |
23 | A new document is now available at `http://localhost:3000/docs/hello`.
24 |
25 | ## Configure the Sidebar
26 |
27 | Docusaurus automatically **creates a sidebar** from the `docs` folder.
28 |
29 | Add metadata to customize the sidebar label and position:
30 |
31 | ```md title="docs/hello.md" {1-4}
32 | ---
33 | sidebar_label: 'Hi!'
34 | sidebar_position: 3
35 | ---
36 |
37 | # Hello
38 |
39 | This is my **first Docusaurus document**!
40 | ```
41 |
42 | It is also possible to create your sidebar explicitly in `sidebars.js`:
43 |
44 | ```js title="sidebars.js"
45 | module.exports = {
46 | tutorialSidebar: [
47 | {
48 | type: 'category',
49 | label: 'Tutorial',
50 | // highlight-next-line
51 | items: ['hello'],
52 | },
53 | ],
54 | };
55 | ```
56 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/assignment.md:
--------------------------------------------------------------------------------
1 | # Assignment Operators
2 |
3 | > An assignment operator assigns a value to its left operand based on the value of its right operand.
4 |
5 |
6 |
7 | ---
8 |
9 | You **may** do chain assignment.
10 |
11 | ```ts
12 | // ok
13 | const a = b = 1
14 | ```
15 |
16 | > Why?
17 |
18 | In JavaScript,
19 | chain assignment is not a good idea because you might introduce global variable if you are not careful.
20 |
21 | However, in TypeScript you don't have this problem because the compilier will spot the error for you 😎.
22 |
23 | ---
24 |
25 | You **should not** have linebreaks before or after `=` in an assignment.
26 |
27 | ```ts
28 | // bad
29 | const foo =
30 | 'foo'
31 |
32 | // bad
33 | const foo
34 | = 'foo'
35 |
36 | // good
37 | const foo = 'foo'
38 | ```
39 |
40 | > Why?
41 |
42 | These incidental linebreaks would throw off tools that rely on comments.
43 |
44 | ```ts
45 | // bad, broke tooling
46 | // istanbul ignore next
47 | // tslint:disable-next-line
48 | const foo =
49 | somethingToIgnore()
50 |
51 | // good
52 | // istanbul ignore next
53 | // tslint:disable-next-line
54 | const foo = somethingToIgnore()
55 | ```
56 |
--------------------------------------------------------------------------------
/page/src/components/GitHubBadge.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@tanstack/react-query'
2 |
3 | export function GitHubBadge() {
4 | const result = useQuery({
5 | queryKey: ['repo-stats'],
6 | queryFn: async ({ signal }) => {
7 | const response = await fetch('https://api.github.com/repos/unional/typescript-blackbook', {
8 | signal
9 | })
10 | return response.json()
11 | }
12 | })
13 |
14 | if (result.data?.stargazers_count === undefined) return <>>
15 |
16 | return (
17 | <>
18 |
38 | >
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/github-page/static/img/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/apps/starlight/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/object-destructuring.md:
--------------------------------------------------------------------------------
1 | # Object Destructuring
2 |
3 | ## Destructuring in Function Argument
4 |
5 | You **should** use object destructuring in function argument to pick out what you need.
6 |
7 | ```ts
8 | // bad
9 | function foo(prop) {
10 | return prop.a + prop.b
11 | }
12 |
13 | // good
14 | function foo({ a, b }) { return a + b }
15 | ```
16 |
17 | > Why?
18 |
19 | When you have a function taking in an object,
20 | the object may contain things that you don't need.
21 |
22 | Using object spread give the reader a clear statement that those are the only properties you are interested in.
23 |
24 | This is expecially valuable if the interface of the parameter is defined by other code.
25 |
26 | > Why not?
27 |
28 | If you do functional programming,
29 | you are likely to use a `context` parameter as the first parameter,
30 | and you need to pass the context down to other functions.
31 |
32 | In this case, you need all properties in the `context` variables,
33 | not only those you use within your function.
34 |
35 | In that case, do object destructuring in the function body.
36 |
37 | ```ts
38 | function foo(context) {
39 | const { io, record } = context
40 | }
41 | ```
42 |
43 | Of course, if the parameter is an instance of a class, or an object that aware of `this`,
44 | then you cannot destructure.
45 |
--------------------------------------------------------------------------------
/docs/pages/04-typescript-syntax/enum.md:
--------------------------------------------------------------------------------
1 | # enum
2 |
3 | > Enums allow us to define a set of named constants.
4 |
5 | -
6 | -
7 |
8 | There are a few special rules about `enum` type:
9 |
10 | - values can be either `string` or `number`
11 |
12 | ```ts
13 | enum map { a = 1, b = 'b' }
14 | ```
15 |
16 | - it is nominal typed
17 |
18 | ```ts
19 | enum JapanVocaloid { miku, luka }
20 |
21 | enum ChinaVocaloid { miku, LuoTianyi, XingChen }
22 |
23 | let myFavoriteVocaloid = ChinaVocaloid.miku
24 |
25 | // error
26 | myFavoriteVocaloid = JapanVocaloid.miku
27 | ```
28 |
29 | ---
30 |
31 | You **should avoid** using `enum`. You **should** use `const` instead.
32 |
33 | ```ts
34 | // bad
35 | enum Color { Red, Green, Blue }
36 |
37 | // good
38 | const COLOR = {
39 | Red: 0,
40 | Green: 1,
41 | Blue: 2
42 | } as const
43 |
44 | // good
45 | const COLOR_RED = 0
46 | const COLOR_GREEN = 1
47 | const COLOR_BLUE = 2
48 | ```
49 |
50 | > Why?
51 |
52 | While `enum` is more compact,
53 | and provide additional nominal protection,
54 | it is a TypeScript syntax.
55 | It goes against our design principles.
56 |
57 | The transpiled code can be different depends on the config options,
58 | and it can get confusing as it may get erased (`const enum`).
59 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript Syntax
2 |
3 | In this chapter,
4 | I will go over syntax and keywords already exist in JavaScript,
5 | and how to work with them in TypeScript environment.
6 |
7 | ## Topics
8 |
9 | - [Array](/docs/pages/02-javascript-syntax/array.md)
10 | - [Arrow Function](/docs/pages/02-javascript-syntax/arrow-function.md)
11 | - [Assignment](/docs/pages/02-javascript-syntax/assignment.md)
12 | - [Async Await](/docs/pages/02-javascript-syntax/async-await.md)
13 | - [Boolean](/docs/pages/02-javascript-syntax/boolean.md)
14 | - [Class](/docs/pages/02-javascript-syntax/class.md)
15 | - [Declaration Statements](/docs/pages/02-javascript-syntax/declaration-statements.md)
16 | - [Decorator](/docs/pages/02-javascript-syntax/decorator.md)
17 | - [Default Parameters](/docs/pages/02-javascript-syntax/default-parameters.md)
18 | - [Error](/docs/pages/02-javascript-syntax/error.md)
19 | - [Function](/docs/pages/02-javascript-syntax/function.md)
20 | - [Module](/docs/pages/02-javascript-syntax/module.md)
21 | - [Object Descructuring](/docs/pages/02-javascript-syntax/object-destructuring.md)
22 | - [Object Literal](/docs/pages/02-javascript-syntax/object-literal.md)
23 | - [Property Accessor](/docs/pages/02-javascript-syntax/property-accessor.md)
24 | - [String](/docs/pages/02-javascript-syntax/string.md)
25 | - [this](/docs/pages/02-javascript-syntax/this.md)
26 |
--------------------------------------------------------------------------------
/page/src/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2 | import { GitHubBadge } from './GitHubBadge'
3 |
4 | const queryClient = new QueryClient()
5 |
6 | export function Header({ active }: { active?: 'blog' | 'blackbook' }) {
7 | return (
8 |
9 |
10 |
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/docs/drafts/modules.md:
--------------------------------------------------------------------------------
1 | # Modules
2 |
3 | ## standard `import`/`export`
4 |
5 | - Always use modules (`import`/`export`) over a non-standard module system. You can always transpile to your preferred module system.
6 |
7 | > Why? Modules are the future, let's start using the future now.
8 |
9 | ```typescript
10 | // bad
11 | const AirbnbStyleGuide = require('./AirbnbStyleGuide');
12 | module.exports = AirbnbStyleGuide.es6;
13 |
14 | // ok
15 | import AirbnbStyleGuide from './AirbnbStyleGuide';
16 | export default AirbnbStyleGuide.es6;
17 |
18 | // best
19 | import { es6 } from './AirbnbStyleGuide';
20 | export default es6;
21 | ```
22 |
23 | ## Wildcard import
24 |
25 | - Do not use wildcard imports.
26 |
27 | > Why? This makes sure you have a single default export.
28 |
29 | ```typescript
30 | // bad
31 | import * as AirbnbStyleGuide from './AirbnbStyleGuide';
32 |
33 | // good
34 | import AirbnbStyleGuide from './AirbnbStyleGuide';
35 | ```
36 |
37 | ## Exporting
38 |
39 | - And do not export directly from an import.
40 |
41 | > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.
42 |
43 | ```typescript
44 | // bad
45 | // filename es6.js
46 | export { es6 as default } from './airbnbStyleGuide';
47 |
48 | // good
49 | // filename es6.js
50 | import { es6 } from './AirbnbStyleGuide';
51 | export default es6;
52 | ```
53 |
--------------------------------------------------------------------------------
/page/src/components/Card.astro:
--------------------------------------------------------------------------------
1 | ---
2 | export interface Props {
3 | title: string;
4 | body: string;
5 | href: string;
6 | }
7 |
8 | const { href, title, body } = Astro.props;
9 | ---
10 |
11 |
22 |
63 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/decorator.md:
--------------------------------------------------------------------------------
1 | # Decorator
2 |
3 | > Decorators make it possible to annotate and modify classes and properties at design time.
4 |
5 | ## When To Use
6 |
7 | You **may** use decorator with some reservation.
8 |
9 | > Why?
10 |
11 | The decorator proposal has been in stage 2 for many years.
12 | While it is actively used as TypeScript and Babel supports it,
13 | it may change in the future.
14 |
15 | ## Hoisting
16 |
17 | When using decorator, you **must** beware of `hoisting`.
18 |
19 | ```ts
20 | import { autoinject } from 'aurelia-dependency-injection'
21 |
22 | @autoinject()
23 | class Depender {
24 | constructor(private dep: Dependent) { }
25 | }
26 |
27 | class Dependent {
28 | }
29 | ```
30 |
31 | > Why?
32 |
33 | Decorator executes at load time, at the point of declaration.
34 | This means all references it uses must be defined prior to the call.
35 |
36 | The following code doesn't work:
37 |
38 | `dep` parameter will receive `undefined` because the variable of class `Dependent` is hoisted but not the initialization.
39 |
40 | Therefore, when the `@autoinject()` is called on the class `Depender`, the `Dependent` variable is still undefined.
41 |
42 | ## References
43 |
44 | -
45 | -
46 | -
47 | -
48 |
--------------------------------------------------------------------------------
/github-page/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-page",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "build:doc": "docusaurus build",
7 | "clear": "docusaurus clear",
8 | "deploy": "docusaurus deploy",
9 | "docusaurus": "docusaurus",
10 | "serve": "docusaurus serve",
11 | "start": "docusaurus start",
12 | "swizzle": "docusaurus swizzle",
13 | "typecheck": "tsc",
14 | "verify": "pnpm build",
15 | "write-heading-ids": "docusaurus write-heading-ids",
16 | "write-translations": "docusaurus write-translations"
17 | },
18 | "dependencies": {
19 | "@docusaurus/core": "2.4.3",
20 | "@docusaurus/plugin-client-redirects": "^2.2.0",
21 | "@docusaurus/preset-classic": "2.4.3",
22 | "@mdx-js/react": "^1.6.22",
23 | "clsx": "^2.0.0",
24 | "prism-react-renderer": "^1.3.5",
25 | "react": "^19.0.0",
26 | "react-dom": "^19.0.0"
27 | },
28 | "devDependencies": {
29 | "@docusaurus/module-type-aliases": "2.4.3",
30 | "@docusaurus/theme-classic": "^2.2.0",
31 | "@docusaurus/types": "^2.2.0",
32 | "@tsconfig/docusaurus": "^2.0.0",
33 | "@types/node": "^18.11.9",
34 | "typescript": "^5.0.0"
35 | },
36 | "browserslist": {
37 | "production": [
38 | ">0.5%",
39 | "not dead",
40 | "not op_mini all"
41 | ],
42 | "development": [
43 | "last 1 chrome version",
44 | "last 1 firefox version",
45 | "last 1 safari version"
46 | ]
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/apps/tutorial/src/styles/global.css:
--------------------------------------------------------------------------------
1 | html {
2 | background-color: #f1f5f9;
3 | font-family: sans-serif;
4 | }
5 |
6 | body {
7 | margin: 0 auto;
8 | width: 100%;
9 | max-width: 80ch;
10 | padding: 1rem;
11 | line-height: 1.5;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
18 | h1 {
19 | margin: 1rem 0;
20 | color: purple;
21 | font-size: 4rem;
22 | }
23 |
24 | /* nav styles */
25 | .hamburger {
26 | padding-right: 20px;
27 | cursor: pointer;
28 | }
29 |
30 | .hamburger .line {
31 | display: block;
32 | width: 40px;
33 | height: 5px;
34 | margin-bottom: 10px;
35 | background-color: #ff9776;
36 | }
37 |
38 | .nav-links {
39 | width: 100%;
40 | top: 5rem;
41 | left: 48px;
42 | background-color: #ff9776;
43 | display: none;
44 | margin: 0;
45 | }
46 |
47 | .nav-links a {
48 | display: block;
49 | text-align: center;
50 | padding: 10px 0;
51 | text-decoration: none;
52 | font-size: 1.2rem;
53 | font-weight: bold;
54 | text-transform: uppercase;
55 | }
56 |
57 | .nav-links a:hover,
58 | .nav-links a:focus {
59 | background-color: #ff9776;
60 | }
61 |
62 | .expanded {
63 | display: unset;
64 | }
65 |
66 | @media screen and (min-width: 636px) {
67 | .nav-links {
68 | margin-left: 5em;
69 | display: block;
70 | position: static;
71 | width: auto;
72 | background: none;
73 | }
74 |
75 | .nav-links a {
76 | display: inline-block;
77 | padding: 15px 20px;
78 | }
79 |
80 | .hamburger {
81 | display: none;
82 | }
83 | }
--------------------------------------------------------------------------------
/.github/workflows/deploy-page.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | on:
4 | # Trigger the workflow every time you push to the `main` branch
5 | # Using a different branch name? Replace `main` with your branch’s name
6 | push:
7 | branches: [main]
8 | # Allows you to run this workflow manually from the Actions tab on GitHub.
9 | workflow_dispatch:
10 |
11 | # Allow this job to clone the repo and create a page deployment
12 | permissions:
13 | contents: read
14 | pages: write
15 | id-token: write
16 |
17 | jobs:
18 | build:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout your repository using git
22 | uses: actions/checkout@v6
23 | - name: Install, build, and upload your site
24 | uses: withastro/action@v5
25 | with:
26 | path: apps/starlight # The root location of your Astro project inside the repository. (optional)
27 | # node-version: 20 # The specific version of Node that should be used to build your site. Defaults to 20. (optional)
28 | # package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional)
29 |
30 | deploy:
31 | needs: build
32 | runs-on: ubuntu-latest
33 | environment:
34 | name: github-pages
35 | url: ${{ steps.deployment.outputs.page_url }}
36 | steps:
37 | - name: Deploy to GitHub Pages
38 | id: deployment
39 | uses: actions/deploy-pages@v4
40 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/error.md:
--------------------------------------------------------------------------------
1 | # Error
2 |
3 | > `Error` objects are thrown when runtime errors occur.
4 | > The `Error` object can also be used as a base object for user-defined exceptions.
5 |
6 | ## Naming convention
7 |
8 | You **should** name your error as describing an invalid condition,
9 | without adding `Error` suffix at the end.
10 |
11 | ```ts
12 | // ok
13 | class FileNotFoundError extends Error { ... }
14 |
15 | // better
16 | class FileNotFound extends Error { ... }
17 | class PluginDuplicated extends Error { ... }
18 | ```
19 |
20 | > Why?
21 |
22 | Having a PascalCased name signifies it is a class.
23 | Making it describes an invalid condition is sufficient to tell that we are dealing with an error.
24 |
25 | ---
26 |
27 | You **should not** name your error as `InvalidSomthing`.
28 |
29 | > Why?
30 |
31 | The word `invalid` does not provide any information and is the same as `error`.
32 | Provide a more specific name to describe the condition.
33 |
34 | ## Base Class
35 |
36 | You **should** inherit your error from the one of the error packages:
37 |
38 | - [`iso-error`](https://www.npmjs.com/package/iso-error)
39 | - [`make-error`](https://www.npmjs.com/package/make-error)
40 | - [`make-error-cause`](https://www.npmjs.com/package/make-error-cause)
41 |
42 | > Why?
43 |
44 | The standard `Error` class does not work with `instanceof`.
45 | All of the packages above works correctly.
46 |
47 | ## References
48 |
49 | -
50 |
--------------------------------------------------------------------------------
/github-page/docusaurus-example/docs/tutorial-extras/manage-docs-versions.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Manage Docs Versions
6 |
7 | Docusaurus can manage multiple versions of your docs.
8 |
9 | ## Create a docs version
10 |
11 | Release a version 1.0 of your project:
12 |
13 | ```bash
14 | npm run docusaurus docs:version 1.0
15 | ```
16 |
17 | The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
18 |
19 | Your docs now have 2 versions:
20 |
21 | - `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
22 | - `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
23 |
24 | ## Add a Version Dropdown
25 |
26 | To navigate seamlessly across versions, add a version dropdown.
27 |
28 | Modify the `docusaurus.config.js` file:
29 |
30 | ```js title="docusaurus.config.js"
31 | module.exports = {
32 | themeConfig: {
33 | navbar: {
34 | items: [
35 | // highlight-start
36 | {
37 | type: 'docsVersionDropdown',
38 | },
39 | // highlight-end
40 | ],
41 | },
42 | },
43 | };
44 | ```
45 |
46 | The docs version dropdown appears in your navbar:
47 |
48 | 
49 |
50 | ## Update an existing version
51 |
52 | It is possible to edit versioned docs in their respective folder:
53 |
54 | - `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
55 | - `docs/hello.md` updates `http://localhost:3000/docs/next/hello`
56 |
--------------------------------------------------------------------------------
/docs/pages/04-typescript-syntax/modules.md:
--------------------------------------------------------------------------------
1 | # Module
2 |
3 | Module in TypeScript has the same semantic meaning as in ES2015.
4 |
5 | The actual definition on module is subtle and complex.
6 | Fortunately, if you follow these simple rules, creating module is relatively straight forward.
7 |
8 | ## Module keyword
9 |
10 | - Do not wrap typings in `declare module "X" {`. Expose using **top-level import / export**
11 |
12 | > Why? `declare module "X" {` will cause name conflict if consumer use two different versions of the same library.
13 | > In TypeScript 1.8, it is used for module augmentation.
14 |
15 | ```ts
16 | // bad
17 | declare module "X" {
18 | export interface A {
19 | // stuff...
20 | };
21 | }
22 |
23 | // good
24 | export interface A {
25 | // stuff...
26 | };
27 | ```
28 |
29 | ## Note
30 |
31 | Prior to TypeScript 1.5, there are two types of modules:
32 |
33 | - Internal module (`declare module X {`)
34 | - External module (`declare module "X" {`)
35 |
36 | In TypeScript 1.5, the term and keyword `namespace` is introduced.
37 | The nomenclature has changed.
38 |
39 | - Internal module -> namespace
40 | - External module -> module
41 |
42 | The `declare module X {` syntax exists for backward compatibility.
43 |
44 | ## Reference
45 |
46 | -
47 | -
48 | -
49 |
--------------------------------------------------------------------------------
/docs/pages/01-introduction/what-is-typescript.md:
--------------------------------------------------------------------------------
1 | # What is TypeScript
2 |
3 | > TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
4 |
5 | It is very active and it is [gaining popularity at a rapid rate](https://www.google.com/trends/explore#q=TypeScript\&cmpt=q\&tz=Etc%2FGMT%2B7)
6 |
7 | You can understand more about TypeScript directly from the [official website](http://www.typescriptlang.org) and the [TypeScript handbook](http://www.typescriptlang.org/docs/handbook/basic-types.html)
8 |
9 | ## Benefits
10 |
11 | - Compatible syntax and semantics as JavaScript.
12 | - Gradual Type system
13 | - Better IDE support
14 | - Support latest ESMCScript syntax
15 |
16 | ## Alternatives: Other Transpilers
17 |
18 | TypeScript allows you to write ES2015+ syntax today by transpiling the code back to ES3/ES5 JavaScript.
19 | There are other transpilers such as [`babel`](http://babeljs.io/) and [`traceur`](https://github.com/google/traceur-compiler) that allows you to do similar things.
20 |
21 | `babel` is pretty much winning the transpiler race against `traceur` and support more new syntax compare to `traceur` and `TypeScript`.
22 | In this front, `TypeScript` is trailing behind because on the added complexity of the type system and does not support plugins as `babel` does.
23 | However, `TypeScript` has the benefit of type system and the ability to transpile to ES3 (`babel` and `traceur` only transpile to ES5).
24 |
25 | ## Alternatives: Type system
26 |
27 | [`flow`](https://flowtype.org/) is a static type checker for JavaScript.
28 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[astro]": {
3 | "editor.defaultFormatter": "astro-build.astro-vscode"
4 | },
5 | "cSpell.enableFiletypes": [
6 | "mdx"
7 | ],
8 | "cSpell.words": [
9 | "antongolub",
10 | "apos",
11 | "astro",
12 | "astrojs",
13 | "astrolib",
14 | "clsx",
15 | "codeql",
16 | "Deno",
17 | "dzharii",
18 | "horiz",
19 | "Rehype",
20 | "stroustrup",
21 | "svgs",
22 | "tabler"
23 | ],
24 | "css.customData": [
25 | "./vscode.tailwind.json"
26 | ],
27 | "emojisense.languages": {
28 | "git-commit": true,
29 | "markdown": true,
30 | "mdx": true,
31 | "plaintext": {
32 | "emojiDecoratorsEnabled": false,
33 | "markupCompletionsEnabled": false
34 | },
35 | "scminput": true
36 | },
37 | "eslint.useFlatConfig": true,
38 | "eslint.validate": [
39 | "javascript",
40 | "javascriptreact",
41 | "astro",
42 | "typescript",
43 | "typescriptreact"
44 | ],
45 | "files.associations": {
46 | "*.excalidraw": "json",
47 | "*.excalidrawlib": "json",
48 | },
49 | "ltex.enabled": [
50 | "bibtex",
51 | "context",
52 | "context.tex",
53 | "html",
54 | "latex",
55 | "markdown",
56 | "mdx",
57 | "org",
58 | "restructuredtext",
59 | "rsweave"
60 | ],
61 | "markdownlint.ignore": ".markdownlintignore",
62 | "prettier.documentSelectors": [
63 | "**/*.astro"
64 | ],
65 | "search.exclude": {
66 | "**/.yarn": true
67 | },
68 | "typescript.tsdk": "node_modules\\typescript\\lib",
69 | "yaml.schemas": {
70 | "./.vscode/astrowind/config-schema.json": "apps/website/src/config.yaml"
71 | }
72 | }
--------------------------------------------------------------------------------
/docs/pages/06-typescript-usage/explicit-vs-implicit-types.md:
--------------------------------------------------------------------------------
1 | # Explicit vs Implicit Types
2 |
3 | One of the main benefit of TypeScript over vanilla JavaScript is its type system.
4 | Does it mean you should type your argument, variables, and return value as much as possible?
5 |
6 | I would suggest you to keep your explicit typings to a minimal.
7 |
8 | We all know that there are pros and cons between statically typed language and dynamically typed language.
9 |
10 | Statically typed language such as Java, C# and C++ provides better communication between API,
11 | and better tooling support,
12 | but they are typically takes more time to write program on it (compare to dynamically typed language).
13 |
14 | Dynamically typed language such as Ruby, Python, and JavaScript are easy to use, quick to code,
15 | but have to pay for the price of relying more on implicit communication of API and less support on tooling.
16 | They have to rely on comments such as JSDoc or testing to communicate its API and behavior to the user.
17 |
18 | The power of TypeScript is that its type system is a gradual type system.
19 | You can omit it if you want, and you can add it in if you desire.
20 | This place TypeScript in between statically typed and dynamically typed language and is able to benefit from both sides.
21 |
22 | You should be using its type system as a communication tool to clarify your API,
23 | and since TypeScript 2.0,
24 | the control flow analysis is getting very good that many times it infers the right type for you.
25 |
26 | Another way to use its type system is to help the TypeScript compiler when it cannot...
27 |
--------------------------------------------------------------------------------
/github-page/docusaurus-example/docs/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Tutorial Intro
6 |
7 | Let's discover **Docusaurus in less than 5 minutes**.
8 |
9 | ## Getting Started
10 |
11 | Get started by **creating a new site**.
12 |
13 | Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
14 |
15 | ### What you'll need
16 |
17 | - [Node.js](https://nodejs.org/en/download/) version 14 or above:
18 | - When installing Node.js, you are recommended to check all checkboxes related to dependencies.
19 |
20 | ## Generate a new site
21 |
22 | Generate a new Docusaurus site using the **classic template**.
23 |
24 | The classic template will automatically be added to your project after you run the command:
25 |
26 | ```bash
27 | npm init docusaurus@latest my-website classic
28 | ```
29 |
30 | You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.
31 |
32 | The command also installs all necessary dependencies you need to run Docusaurus.
33 |
34 | ## Start your site
35 |
36 | Run the development server:
37 |
38 | ```bash
39 | cd my-website
40 | npm run start
41 | ```
42 |
43 | The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.
44 |
45 | The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.
46 |
47 | Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.
48 |
--------------------------------------------------------------------------------
/github-page/docs/guidelines/project/package_json.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: package-json
3 | title: "package.json"
4 | authors: [unional]
5 | tags: [project, typescript, tsconfig]
6 | ---
7 |
8 | ## The `files` field
9 |
10 | The `files` field is used to specify which files and folders to be included in the publish package.
11 |
12 | You **should** always specify the `files` field.
13 |
14 | > Why?
15 |
16 | By default, files not excluded by `.gitignore` (or `.npmignore`) are included, which is not what you want.
17 | Also, files that are excluded by `.gitignore` are not included, which is likely also not what you want.
18 |
19 | So it is always better to be explicit and control them yourself.
20 |
21 | For example, if the `files` field is removed from [type-plus],
22 |
23 | files like `.changeset/*`, `.github/*`, `.vscode/*` are included,
24 | while `cjs/*` and `esm/*` are not.
25 |
26 | ---
27 |
28 | You **should** use `files` field to exclude test files.
29 |
30 | For example, add this to your `files` field:
31 |
32 | ```json5
33 | {
34 | "files": [
35 | // your package files
36 | "cjs",
37 | "esm",
38 | "testing",
39 | "ts",
40 | // exclude test files
41 | "!**/*.{spec,test,unit,accept,integrate,system,perf,stress}.*"
42 | ]
43 | }
44 | ```
45 |
46 | > Why?
47 |
48 | Doing this allows you to keep your tsconfig setup simple.
49 | You will always compile all files, including your test files.
50 |
51 | This ensures that your test files does not contain any syntax error.
52 |
53 | -
54 |
55 | [type-plus]: https://github.com/unional/type-plus
56 |
--------------------------------------------------------------------------------
/docs/pages/09-tooling/jest.md:
--------------------------------------------------------------------------------
1 | # jest
2 |
3 | [`jest`](https://jestjs.io) is a test runner developed by Facebook.
4 | It is arguably the best test runner out there right now.
5 |
6 | One of the most powerful feature of `jest` is its watch mode.
7 |
8 | In order to use `jest` with TypeScript, you can use `ts-jest`.
9 |
10 | ## ts-jest Compiler disgnostics
11 |
12 | By default, `ts-jest` presets to enable compiler diagnostics.
13 | This means if your project has TypeScript error, running `jest` will catch it.
14 |
15 | - Do not enable compiler diagnostics for local tests.
16 |
17 | > Why? You run tests continuously during development.
18 | > Enabling compiler disgnostics forces you fix all your TypeScript errors before you can test the runtime errors.
19 | > This prohibit you from quickly commenting out code to test specific logics.
20 |
21 | - Do enable compiler diagnostics for CI tests
22 |
23 | > Why? During CI, you want to minimize work by running `tsc` to build actual code and noter test code.
24 | > Enabling compiler diagnostics in CI ensure your tests are also free to TypeScript errors.
25 |
26 | ## jest-watch-suspend
27 |
28 | - Use `jest-watch-suspend` to suspend/resume test during watch mode.
29 |
30 | > Why? There are many use cases that this is valuable.
31 | > For example, you are making some changes to your code and want to change the filtering at the same time.
32 | > For other scenarios, check out [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend) README.
33 |
34 | ## jest-watch-toggle-config
35 |
36 | - Use `jest-watch-toggle-config` to add `verbose` and `coverage` functionality to watch mode.
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | bower_components
28 | jspm_packages
29 | node_modules
30 |
31 | # Optional npm cache directory
32 | .npm
33 |
34 | # Optional REPL history
35 | .node_repl_history
36 |
37 | # webstorm
38 | .idea
39 |
40 | # typings
41 | /typings/
42 |
43 | # yarn
44 | .pnp.*
45 | .yarn/*
46 | !.yarn/patches
47 | !.yarn/plugins
48 | !.yarn/releases
49 | !.yarn/sdks
50 | !.yarn/versions
51 |
52 | # output folder
53 | .turbo
54 | out/
55 | lib/
56 | ._.DS_Store
57 | tsconfig.tsbuildinfo
58 |
59 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
60 |
61 | # dependencies
62 | node_modules
63 | .pnp
64 | .pnp.js
65 |
66 | # testing
67 | coverage
68 |
69 | # next.js
70 | .next/
71 | out/
72 | build
73 |
74 | # macOS-specific files
75 | .DS_Store
76 |
77 | # misc
78 | *.pem
79 |
80 | # debug
81 | npm-debug.log*
82 | yarn-debug.log*
83 | yarn-error.log*
84 | .pnpm-debug.log*
85 |
86 | # environment variables
87 | .env
88 | .env.production
89 | .env.local
90 | .env.development.local
91 | .env.test.local
92 | .env.production.local
93 |
94 | # turbo
95 | .turbo
96 |
97 | .astro
98 |
--------------------------------------------------------------------------------
/docs/drafts/file-structures.md:
--------------------------------------------------------------------------------
1 | # File and Folder Structures
2 |
3 | ## File line endings
4 |
5 | - Keep line ending in `LF` if your team work on both windows and \*nix.
6 |
7 | > Why? `CRLF` would cause grief on \*nix system (double blank lines)
8 |
9 | ## File EOF (End of line)
10 |
11 | - Except docs such as `.md`, all files should end with a newline.
12 |
13 | ```ts
14 | // bad < Why? This ensures readability and maintainability.
25 |
26 | - However, this is not enforce by tooling.
27 |
28 | > Why? Avoid unnecessary mental block when this rule conflicts with others.
29 |
30 | ```ts
31 | // bad
32 | const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';
33 |
34 | // bad
35 | $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
36 |
37 | // good
38 | const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
39 | 'Whatever wizard constrains a helpful ally. The counterpart ascends!';
40 |
41 | // good
42 | $.ajax({
43 | method: 'POST',
44 | url: 'https://airbnb.com/',
45 | data: { name: 'John' },
46 | })
47 | .done(() => console.log('Congratulations!'))
48 | .fail(() => console.log('You have failed this city.'));
49 | ```
50 |
--------------------------------------------------------------------------------
/apps/starlight/src/content/docs/guidelines/project/package-json.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: package-json
3 | title: "package.json"
4 | authors: [unional]
5 | tags: [project, typescript, tsconfig]
6 | ---
7 |
8 | ## The `files` field
9 |
10 | The `files` field is used to specify which files and folders to be included in the publish package.
11 |
12 | You **should** always specify the `files` field.
13 |
14 | > Why?
15 |
16 | By default, files not excluded by `.gitignore` (or `.npmignore`) are included, which is not what you want.
17 | Also, files that are excluded by `.gitignore` are not included, which is likely also not what you want.
18 |
19 | So it is always better to be explicit and control them yourself.
20 |
21 | For example, if the `files` field is removed from [type-plus],
22 |
23 | files like `.changeset/*`, `.github/*`, `.vscode/*` are included,
24 | while `cjs/*` and `esm/*` are not.
25 |
26 | ---
27 |
28 | You **should** use `files` field to exclude test files.
29 |
30 | For example, add this to your `files` field:
31 |
32 | ```json5
33 | {
34 | "files": [
35 | // your package files
36 | "cjs",
37 | "esm",
38 | "testing",
39 | "ts",
40 | // exclude test files
41 | "!**/*.{spec,test,unit,accept,integrate,system,perf,stress}.*"
42 | ]
43 | }
44 | ```
45 |
46 | > Why?
47 |
48 | Doing this allows you to keep your tsconfig setup simple.
49 | You will always compile all files, including your test files.
50 |
51 | This ensures that your test files does not contain any syntax error.
52 |
53 | - [Files](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#files)
54 |
55 | [type-plus]: https://github.com/unional/type-plus
56 |
--------------------------------------------------------------------------------
/docs/pages/04-typescript-syntax/interfaces.md:
--------------------------------------------------------------------------------
1 | # Interface
2 |
3 | ## interface vs type alias
4 |
5 | You **should** prefer type alias over interface.
6 |
7 | > Why?
8 |
9 | Interface syntax is carried over from other Object Oriented languages such as Java and C#.
10 |
11 | The difference between `type` and `interface` is become very small.
12 | `type` can do most of `interface` can do except merging definitions.
13 |
14 | On the other hand, `type` can do quite a lot more compare to `interface`,
15 | and the syntax is more concise and straight forward.
16 |
17 | > Why not?
18 |
19 | While `type` is more expressive and generally more powerful,
20 | `interface` is more performant.
21 |
22 | So if your type is simple, you can use `interface` instead.
23 |
24 | ### Reference
25 |
26 | -
27 | -
28 |
29 | ## Naming
30 |
31 | - Name interface in PascalCase.
32 | - Do not prefix interface with `I`.
33 |
34 | ```ts
35 | // bad
36 | interface myInterface { }
37 | interface IDisposable { }
38 |
39 | // good
40 | interface MyInterface { }
41 | interface Disposable { }
42 | ```
43 |
44 | > Why?
45 |
46 | The concept of an interface in TypeScript is much more broad than in C# or Java,
47 | the `IFoo` naming convention is not broadly useful.
48 |
49 | [Microsoft Coding Guideline](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines)
50 |
51 | [Why?](https://github.com/Microsoft/TypeScript-Handbook/blob/4439d3101283adb38dabb2a4c39726986d6bbcb2/pages/Writing%20Definition%20Files.md#naming-conventions)
52 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/boolean.md:
--------------------------------------------------------------------------------
1 | # Boolean
2 |
3 | The type of boolean in TypeScript is `boolean`.
4 | It also have boolean literal types `true` and `false`
5 |
6 | ---
7 |
8 | Predicate functions **should** return `boolean`.
9 |
10 | ```ts
11 | // bad
12 | function hasValue(value: any) {
13 | return value
14 | }
15 |
16 | // good
17 | function hasValue(value: any) {
18 | return value !== undefined
19 | }
20 | ```
21 |
22 | > Why?
23 |
24 | Relying on implicit conversion is dangerous.
25 | Always be explicit.
26 |
27 | ```ts
28 | hasValue(0) ? true : false // false
29 | hasValue(false) ? true : false // false
30 | hasValue('') ? true : false // false
31 | hasValue(Symbol()) ? true : false // false
32 | hasValue(Infinity) ? true : false // false
33 | // but
34 | new Boolean(Infinity) // true !!
35 | ```
36 |
37 | ---
38 |
39 | When converting value to boolean, you **should** use double not (`!!`) operator.
40 |
41 | ```ts
42 | const value = false
43 | // bad
44 | const b = new Boolean(value)
45 | if (b) { /* executed! */ }
46 |
47 | // so so
48 | const c = Boolean(value)
49 | if (c) { /* not executed */ }
50 |
51 | // good
52 | const d = !!value
53 | if (d) { /* not executed */ }
54 | ```
55 |
56 | > Why?
57 |
58 | In 99.99999% of the time,
59 | you do not even know the existence of the boolean object wrapper `Boolean`.
60 | It is different then the `boolean` you use days in days out.
61 |
62 | So don't confuse yourself and your reader by mentioning it in your code when not necessary.
63 |
64 | ## References
65 |
66 | -
67 | -
68 |
--------------------------------------------------------------------------------
/apps/starlight/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { defineConfig } from 'astro/config';
3 | import starlight from '@astrojs/starlight';
4 |
5 | import tailwind from '@astrojs/tailwind';
6 |
7 | // https://astro.build/config
8 | export default defineConfig({
9 | site: 'https://unional.github.io/',
10 | base: 'typescript-blackbook',
11 | integrations: [starlight({
12 | title: 'TypeScript Blackbook',
13 | favicon: './src/assets/logo.svg',
14 | logo: { src:'./src/assets/logo.svg'},
15 | social: [
16 | {
17 | label: 'X',
18 | icon: 'x.com',
19 | href: 'https://x.com/unional',
20 | },
21 | {
22 | label: 'Discord',
23 | icon: 'discord',
24 | href: 'https://discord.gg/RwzcFpN5fv',
25 | },
26 | {
27 | label: 'GitHub',
28 | icon: 'github',
29 | href: 'https://github.com/unional',
30 | },
31 | {
32 | label: 'Stack Overflow',
33 | icon: 'stackOverflow',
34 | href: 'https://stackoverflow.com/users/3505900/unional',
35 | },
36 | {
37 | label: 'YouTube',
38 | icon: 'youtube',
39 | href: 'https://www.youtube.com/@cyberuni',
40 | },
41 | ],
42 | sidebar: [
43 | {
44 | label: 'Guides',
45 | items: [
46 | // Each item here is one entry in the navigation menu.
47 | { slug: 'guides/welcome' },
48 | ],
49 | },
50 | ],
51 | }), tailwind()]
52 | });
--------------------------------------------------------------------------------
/docs/pages/09-tooling/vscode.md:
--------------------------------------------------------------------------------
1 | # Visual Studio Code
2 |
3 | ## User snippets
4 |
5 | TypeScript
6 |
7 | ```json
8 | {
9 | "console.log": {
10 | "prefix": "log",
11 | "body": [
12 | "console.log($1)"
13 | ]
14 | },
15 | "istanbul ignore": {
16 | "prefix": "istanbul ignore next",
17 | "body": [
18 | "// istanbul ignore next"
19 | ]
20 | },
21 | "Import es6 module": {
22 | "prefix": "import from",
23 | "body": [
24 | "import $2 from '${1:module}'$0"
25 | ],
26 | "description": "Import es6 module."
27 | },
28 | "Import cjs module": {
29 | "prefix": "import require",
30 | "body": [
31 | "import $2 = require('${1:module}')$0"
32 | ],
33 | "description": "Import cjs module."
34 | },
35 | "action": {
36 | "prefix": "action",
37 | "body": [
38 | "export const ${1:Custom}ActionType = '${2:module/action}'",
39 | "export function create$1Action(${4:args}) {",
40 | " return createAction($1ActionType$5)",
41 | "}",
42 | "export function is$1Action(action: any): action is FluxStandardAction<${6:any}, ${7:any}> {",
43 | " return action.type === $1ActionType",
44 | "}"
45 | ]
46 | },
47 | "import ava": {
48 | "prefix": "import ava",
49 | "body": [
50 | "import { test } from 'ava'",
51 | "${0}"
52 | ]
53 | },
54 | "async ava test": {
55 | "prefix": "ava async",
56 | "body": [
57 | "test('${1:description}', async t => {",
58 | " $0",
59 | "})"
60 | ]
61 | },
62 | "async jest test": {
63 | "prefix": "jest async",
64 | "body": [
65 | "test('${1:description}', async () => {",
66 | " $0",
67 | "})"
68 | ]
69 | }
70 | }
71 | ```
72 |
--------------------------------------------------------------------------------
/docs/drafts/commas.md:
--------------------------------------------------------------------------------
1 | # Commas
2 |
3 | ## Leading commas
4 |
5 | - **Nope.**
6 |
7 | ```typescript
8 | // bad
9 | const story = [
10 | once
11 | , upon
12 | , aTime
13 | ];
14 |
15 | // good
16 | const story = [
17 | once,
18 | upon,
19 | aTime,
20 | ];
21 |
22 | // bad
23 | const hero = {
24 | firstName: 'Ada'
25 | , lastName: 'Lovelace'
26 | , birthYear: 1815
27 | , superPower: 'computers'
28 | };
29 |
30 | // good
31 | const hero = {
32 | firstName: 'Ada',
33 | lastName: 'Lovelace',
34 | birthYear: 1815,
35 | superPower: 'computers',
36 | };
37 | ```
38 |
39 | ## Trailing commas
40 |
41 | - **Yup.**
42 |
43 | tslint:
44 |
45 | ```js
46 | "trailing-comma": [
47 | true,
48 | {
49 | "singleline": false,
50 | "multiline": true
51 | }
52 | ]
53 | ```
54 |
55 | > Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don't have to worry about the [trailing comma problem](es5/README.md#commas) in legacy browsers.
56 |
57 | ```typescript
58 | // bad - git diff without trailing comma
59 | const hero = {
60 | firstName: 'Florence',
61 | - lastName: 'Nightingale'
62 | + lastName: 'Nightingale',
63 | + inventorOf: ['coxcomb graph', 'modern nursing']
64 | };
65 |
66 | // good - git diff with trailing comma
67 | const hero = {
68 | firstName: 'Florence',
69 | lastName: 'Nightingale',
70 | + inventorOf: ['coxcomb chart', 'modern nursing'],
71 | };
72 |
73 | // bad
74 | const hero = {
75 | firstName: 'Dana',
76 | lastName: 'Scully'
77 | };
78 |
79 | const heroes = [
80 | 'Batman',
81 | 'Superman'
82 | ];
83 |
84 | // good
85 | const hero = {
86 | firstName: 'Dana',
87 | lastName: 'Scully',
88 | };
89 |
90 | const heroes = [
91 | 'Batman',
92 | 'Superman',
93 | ];
94 | ```
95 |
--------------------------------------------------------------------------------
/docs/pages/typings/tslint.md:
--------------------------------------------------------------------------------
1 | # tslint configuration
2 |
3 | You can use this configuration by installing [`tslint-config-typings`](https://github.com/typings/tslint-config-typings) (requires `tslint@3.7.0+`)
4 |
5 | ```sh
6 | npm install -D tslint tslint-config-typings
7 | ```
8 |
9 | ```js
10 | // your tslint.json
11 | {
12 | "extends": "tslint-config-typings",
13 | "rules": {
14 | // your customization
15 | }
16 | }
17 | ```
18 |
19 | ## Rules
20 |
21 | This style follows the [`default style`](../default/tslint.md) if not specified below.
22 |
23 | ### member-ordering (native)
24 |
25 | `"member-ordering": [false]`
26 |
27 | - Follow order in the source package documentation.
28 |
29 | ### no-empty (native)
30 |
31 | `"no-empty": false`
32 |
33 | - When testing the typings, it is quite common to create empty functions.
34 |
35 | ### no-internal-module (native)
36 |
37 | `"no-internal-module": true`
38 |
39 | - There is a lot of typings written with `declare module X {`. Since TypeScript 1.6 `declare namespace X {` is preferred.
40 | - `declare module X {` could be deprecated in the future.
41 |
42 | ### no-require-imports (native)
43 |
44 | `"no-require-imports": false`
45 |
46 | - typings would need to use `import = require()` to get definitions written with `export =` syntax.
47 |
48 | ### no-string-literal (native)
49 |
50 | `"no-string-literal": true`
51 |
52 | - Unlike in the default style, there is no need to support the dynamic use case.
53 |
54 | ### object-literal-sort-keys (native)
55 |
56 | `"object-literal-sort-keys": false`
57 |
58 | - Allow the typings to follow the order as in the source package documentation.
59 |
60 | ## References
61 |
62 | - native:
63 | - eslint:
64 |
--------------------------------------------------------------------------------
/docs/pages/04-typescript-syntax/namespaces-and-modules.md:
--------------------------------------------------------------------------------
1 | # Namespace and Module
2 |
3 | When declaring a module (or namespace), there are two options:
4 |
5 | - declaration wrapped in `declare {namespace,module} {`, or
6 | - top-level declaration
7 |
8 | > Top-level declarations in a source file with no top-level import or export declarations belong to the global namespace.
9 | > Top-level declarations in a source file with one or more top-level import or export declarations belong to the module represented by that source file. ([link](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#23-declarations), need to scroll down a bit)
10 |
11 | ## Module
12 |
13 | - Do not wrap typings in `declare module "X" {`.\\
14 | Expose as **top-level declaration**
15 |
16 | > Why?
17 |
18 | `declare module "X" {` will cause name conflict if consumer use two different version of the same library.
19 |
20 | ```ts
21 | // bad
22 | declare module "X" {
23 | export interface A {
24 | // stuff...
25 | };
26 | }
27 |
28 | // good
29 | export interface A {
30 | // stuff...
31 | };
32 | ```
33 |
34 | ## Note
35 |
36 | Prior to TypeScript 1.5, there are two types of modules:
37 |
38 | - Internal module (`declare module X {`)
39 | - External module (`declare module "X" {`)
40 |
41 | In TypeScript 1.5, the term and keyword `namespace` is introduced.
42 | The nomenclature has changed.
43 |
44 | - Internal module -> namespace
45 | - External module -> module
46 |
47 | The `declare module X {` syntax exists for backward compatibility.
48 |
49 | ## Reference
50 |
51 | -
52 | -
53 | -
54 |
--------------------------------------------------------------------------------
/docs/drafts/destructuring.md:
--------------------------------------------------------------------------------
1 | # Destructuring
2 |
3 | ## Object destructuring
4 |
5 | - Use object destructuring when accessing and using multiple properties of an object. jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring)
6 |
7 | > Why? Destructuring saves you from creating temporary references for those properties.
8 |
9 | ```typescript
10 | // bad
11 | function getFullName(user) {
12 | const firstName = user.firstName;
13 | const lastName = user.lastName;
14 |
15 | return `${firstName} ${lastName}`;
16 | }
17 |
18 | // good
19 | function getFullName(user) {
20 | const { firstName, lastName } = user;
21 | return `${firstName} ${lastName}`;
22 | }
23 |
24 | // best
25 | function getFullName({ firstName, lastName }) {
26 | return `${firstName} ${lastName}`;
27 | }
28 | ```
29 |
30 | ## Array destructuring
31 |
32 | - Use array destructuring. jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring)
33 |
34 | ```typescript
35 | const arr = [1, 2, 3, 4];
36 |
37 | // bad
38 | const first = arr[0];
39 | const second = arr[1];
40 |
41 | // good
42 | const [first, second] = arr;
43 | ```
44 |
45 | ## Multiple return values
46 |
47 | - Use object destructuring for multiple return values, not array destructuring.
48 |
49 | > Why? You can add new properties over time or change the order of things without breaking call sites.
50 |
51 | ```typescript
52 | // bad
53 | function processInput(input) {
54 | // then a miracle occurs
55 | return [left, right, top, bottom];
56 | }
57 |
58 | // the caller needs to think about the order of return data
59 | const [left, __, top] = processInput(input);
60 |
61 | // good
62 | function processInput(input) {
63 | // then a miracle occurs
64 | return { left, right, top, bottom };
65 | }
66 |
67 | // the caller selects only the data they need
68 | const { left, right } = processInput(input);
69 | ```
70 |
--------------------------------------------------------------------------------
/.cursor/rules/guidelines/voice_and_tone.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description: Voice, tone, and writing style guidelines for TypeScript Blackbook. Use when writing or editing any content in the repository.
3 | globs: "**/*.md","**/*.mdx","**/*.mdc"
4 | alwaysApply: true
5 | ---
6 |
7 | # ✍️ Voice & Tone
8 |
9 | ## Authorial Persona
10 |
11 | - **Persona** → "Seasoned TypeScript mentor sharing practical insights"
12 | - **Tone** → direct, practical, educational, authoritative but not prescriptive
13 | - **Point of view** → second person ("you") to address the reader directly
14 | - **Voice** → conversational but professional, with personal insights when relevant
15 |
16 | ## Writing Style
17 |
18 | - **Tense** → present tense for explanations, imperative for instructions
19 | - **Sentence length** → concise (2-4 sentences per paragraph typically)
20 | - **Approach** → explain "why" not just "what" - always include reasoning
21 | - **Focus** → practical benefits and real-world applications
22 |
23 | ## Recommendation Language
24 |
25 | Use clear, emphasized recommendation language:
26 |
27 | - **You **should**** - for best practices and recommended approaches
28 | - **You **can**** - for optional but valid approaches
29 | - **You **should not**** - for antipatterns to avoid
30 | - **You **do not**** - for things that are unnecessary
31 |
32 | ## Prohibited
33 |
34 | - Jokes or colloquialisms that distract from the technical content
35 | - Emoji except when conveying status (✅ Good, ❌ Bad)
36 | - Vague statements without concrete examples
37 | - Recommendations without "Why?" explanations
38 |
39 | ## Required Rules
40 |
41 | The AI agent should read and follow these rules along with the subject rule:
42 |
43 | - **[Syntax Conventions](mdc:.cursor/rules/guidelines/syntax_conventions.mdc)**: For Markdown formatting and code block standards
44 | - **[Article Template](mdc:.cursor/rules/templates/article_template.mdc)**: For content structure
45 |
--------------------------------------------------------------------------------
/github-page/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Welcome
6 |
7 | Welcome to TypeScript Blackbook
8 |
9 | This book focus on *how to get the most out of TypeScript with minimal effort*.
10 |
11 | To achieve that goal,
12 | following some coding styles is a good place to start.
13 |
14 | However, just following *what to do* can only go so far.
15 | You need to know *why* should you write code in certain way,
16 | *what* are the *trade-offs* you are making,
17 | as well as the design and the limitations of the language itself,
18 | so that you have the right *mindset* and *approach* the problem and come to a solution effectively.
19 |
20 | Therefore, this book covers more than just style guide and best practices.
21 |
22 | It needs to cover everything related to TypeScript in order to achieve that goal.
23 |
24 | This book is organized into a few sections:
25 |
26 | - How to TypeScript: Designs, limitations, and approach
27 | - TypeScript (and JavaScript) syntax and features
28 | - Coding Styles and Guidelines
29 | - Supporting Tools
30 |
31 | Learning everything about TypeScript is not easy.
32 |
33 | The language itself is pretty complex,
34 | and both TypeScript and JavaScript evolves at a rapid pace.
35 | So it can be quite overwhelming if you are just starting out.
36 |
37 | I would recommend having a quick read through of the [How to TypeScript] section,
38 | and then check out the [Supporting Tools] section to find out how to set up your project,
39 | and use the rest of the book for reference as you need them.
40 |
41 | Having that said,
42 | I'm in the process of updating this book.
43 |
44 | Most of the information are still in their old format.
45 | So please head over to the [GitHub repo] to look for the original content for the time being.
46 |
47 | [GitHub repo]: https://github.com/unional/typescript-guidelines
48 | [TypeScript Handbook]: https://www.typescriptlang.org/docs/handbook/intro.html
49 |
--------------------------------------------------------------------------------
/github-page/docs/typescript_features/_type_guard.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Type guard"
3 | authors: [unional]
4 | tags: [typescript]
5 | ---
6 |
7 | [User-defined type guard functions][type_guard] is a function which its return type is specified as `x as T`.
8 |
9 | It is introduced in TypeScript 1.6.
10 |
11 | For example:
12 |
13 | ```ts
14 | function isBool(x: unknown): x is boolean {
15 | return typeof x === 'boolean';
16 | }
17 |
18 | let value = 'true' as unknown
19 |
20 | if (isBool(value)) {
21 | // `value` is narrowed to boolean
22 | }
23 | ```
24 |
25 | ---
26 |
27 | You **do not** need to write type guard functions for simple cases.
28 |
29 | > Why?
30 |
31 | TypeScript control flow analysis recognize many basic patterns and perform narrowing automatically.
32 |
33 | For example:
34 |
35 | ```ts
36 | let value: number | string = 123
37 |
38 | if (typeof value === 'number') {
39 | // `value` is narrowed to number
40 | }
41 | ```
42 |
43 | [Playground](https://www.typescriptlang.org/play?#code/DYUwLgBAbghsCuIBcEIDt4FsBGIBOEAPqgM5h4CWaA5hALwQCMATAMwCwAUFxQGYQAKMAE8ADiAD2-WAhD06DAOQYc+RQEoIAby4BIAPT6IAAxmJjECiXQw8eCQHcQAEwhgJ6LLjxcAvlyA)
44 |
45 | ---
46 |
47 | You **can** use `isType` from [type-plus] for one-off assertion functions (especially if you are already using it).
48 |
49 | > Why?
50 |
51 | In many cases, you only need to do type guard for a few specific cases.
52 |
53 | If you are already using [type-plus],
54 | you can use the `isType()` generic type guard so that you don't have to break your flow and add an addition function.
55 |
56 | ```ts
57 | const { data } = useQuery(...)
58 |
59 | if (isType(data, v => ...predicate...)) {
60 | // `data` is narrowed to `YourData`
61 | }
62 | ```
63 |
64 | [assertion_functions]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions
65 | [NodeJS]: https://nodejs.org/
66 | [type-plus]: https://github.com/unional/type-plus
67 |
--------------------------------------------------------------------------------
/apps/starlight/src/content/docs/typescript-features/_type_guard.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Type guard"
3 | authors: [unional]
4 | tags: [typescript]
5 | ---
6 |
7 | [User-defined type guard functions][type_guard] is a function which its return type is specified as `x as T`.
8 |
9 | It is introduced in TypeScript 1.6.
10 |
11 | For example:
12 |
13 | ```ts
14 | function isBool(x: unknown): x is boolean {
15 | return typeof x === 'boolean';
16 | }
17 |
18 | let value = 'true' as unknown
19 |
20 | if (isBool(value)) {
21 | // `value` is narrowed to boolean
22 | }
23 | ```
24 |
25 | ---
26 |
27 | You **do not** need to write type guard functions for simple cases.
28 |
29 | > Why?
30 |
31 | TypeScript control flow analysis recognize many basic patterns and perform narrowing automatically.
32 |
33 | For example:
34 |
35 | ```ts
36 | let value: number | string = 123
37 |
38 | if (typeof value === 'number') {
39 | // `value` is narrowed to number
40 | }
41 | ```
42 |
43 | [Playground](https://www.typescriptlang.org/play?#code/DYUwLgBAbghsCuIBcEIDt4FsBGIBOEAPqgM5h4CWaA5hALwQCMATAMwCwAUFxQGYQAKMAE8ADiAD2-WAhD06DAOQYc+RQEoIAby4BIAPT6IAAxmJjECiXQw8eCQHcQAEwhgJ6LLjxcAvlyA)
44 |
45 | ---
46 |
47 | You **can** use `isType` from [type-plus] for one-off assertion functions (especially if you are already using it).
48 |
49 | > Why?
50 |
51 | In many cases, you only need to do type guard for a few specific cases.
52 |
53 | If you are already using [type-plus],
54 | you can use the `isType()` generic type guard so that you don't have to break your flow and add an addition function.
55 |
56 | ```ts
57 | const { data } = useQuery(...)
58 |
59 | if (isType(data, v => ...predicate...)) {
60 | // `data` is narrowed to `YourData`
61 | }
62 | ```
63 |
64 | [assertion_functions]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions
65 | [NodeJS]: https://nodejs.org/
66 | [type-plus]: https://github.com/unional/type-plus
67 |
--------------------------------------------------------------------------------
/apps/starlight/src/content/docs/guides/welcome.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Welcome
3 | description: Welcome old and new readers to TypeScript Blackbook.
4 | ---
5 |
6 | Welcome to TypeScript Blackbook.
7 |
8 | This book focus on *how to get the most out of TypeScript with minimal effort*.
9 |
10 | To achieve that goal,
11 | following some coding styles is a good place to start.
12 |
13 | However, just following *what to do* can only go so far.
14 | You need to know *why* should you write code in certain way,
15 | *what* are the *trade-offs* you are making,
16 | as well as the design and the limitations of the language itself,
17 | so that you have the right *mindset* and *approach* the problem and come to a solution effectively.
18 |
19 | Therefore, this book covers more than just style guide and best practices.
20 |
21 | It needs to cover everything related to TypeScript in order to achieve that goal.
22 |
23 |
29 |
30 | Learning everything about TypeScript is not easy.
31 |
32 | The language itself is pretty complex,
33 | and both TypeScript and JavaScript evolves at a rapid pace.
34 | So it can be quite overwhelming if you are just starting out.
35 |
36 | I would recommend having a quick read through of the [How to TypeScript] section,
37 | and then check out the [Supporting Tools] section to find out how to set up your project,
38 | and use the rest of the book for reference as you need them.
39 |
40 | Having that said,
41 | I'm in the process of updating this book.
42 |
43 | Most of the information are still in their old format.
44 | So please head over to the [GitHub repo] to look for the original content for the time being.
45 |
46 | [GitHub repo]: https://github.com/unional/typescript-blackbook
47 | [TypeScript Handbook]: https://www.typescriptlang.org/docs/handbook/intro.html
48 |
--------------------------------------------------------------------------------
/github-page/docusaurus-example/docs/tutorial-extras/translate-your-site.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Translate your site
6 |
7 | Let's translate `docs/intro.md` to French.
8 |
9 | ## Configure i18n
10 |
11 | Modify `docusaurus.config.js` to add support for the `fr` locale:
12 |
13 | ```js title="docusaurus.config.js"
14 | module.exports = {
15 | i18n: {
16 | defaultLocale: 'en',
17 | locales: ['en', 'fr'],
18 | },
19 | };
20 | ```
21 |
22 | ## Translate a doc
23 |
24 | Copy the `docs/intro.md` file to the `i18n/fr` folder:
25 |
26 | ```bash
27 | mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
28 |
29 | cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
30 | ```
31 |
32 | Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
33 |
34 | ## Start your localized site
35 |
36 | Start your site on the French locale:
37 |
38 | ```bash
39 | npm run start -- --locale fr
40 | ```
41 |
42 | Your localized site is accessible at `http://localhost:3000/fr/` and the `Getting Started` page is translated.
43 |
44 | :::caution
45 |
46 | In development, you can only use one locale at a same time.
47 |
48 | :::
49 |
50 | ## Add a Locale Dropdown
51 |
52 | To navigate seamlessly across languages, add a locale dropdown.
53 |
54 | Modify the `docusaurus.config.js` file:
55 |
56 | ```js title="docusaurus.config.js"
57 | module.exports = {
58 | themeConfig: {
59 | navbar: {
60 | items: [
61 | // highlight-start
62 | {
63 | type: 'localeDropdown',
64 | },
65 | // highlight-end
66 | ],
67 | },
68 | },
69 | };
70 | ```
71 |
72 | The locale dropdown now appears in your navbar:
73 |
74 | 
75 |
76 | ## Build your localized site
77 |
78 | Build your site for a specific locale:
79 |
80 | ```bash
81 | npm run build -- --locale fr
82 | ```
83 |
84 | Or build your site to include all the locales at once:
85 |
86 | ```bash
87 | npm run build
88 | ```
89 |
--------------------------------------------------------------------------------
/.cursor/rules/project/repository_structure.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description: Repository structure and organization for TypeScript Blackbook. Use when understanding or modifying the project structure, monorepo layout, or content organization.
3 | globs: "**/*"
4 | alwaysApply: false
5 | ---
6 |
7 | # 🏗️ Repository Structure
8 |
9 | ## Overview
10 |
11 | TypeScript Blackbook is organized as a monorepo containing documentation, examples, and supporting tools.
12 |
13 | ## Key Directories
14 |
15 | ### Documentation
16 |
17 | - `apps/starlight/` - Starlight-based documentation site
18 | - `src/content/docs/` - Main documentation content
19 | - `src/content/blogs/` - Blog posts
20 |
21 | ### Legacy Content
22 |
23 | - `docs/` - Legacy documentation structure (being migrated)
24 | - `github-page/` - Previous Docusaurus-based site
25 |
26 | ### Configuration
27 |
28 | - `.cursor/rules/` - Cursor AI rules and guidelines
29 | - Root-level config files for TypeScript, ESLint, Prettier, etc.
30 |
31 | ## Content Organization
32 |
33 | ### Documentation Structure
34 |
35 | Documentation follows a hierarchical structure:
36 |
37 | - **Guides** - Step-by-step tutorials and how-to guides
38 | - **Reference** - API references and technical details
39 | - **Examples** - Code examples and demonstrations
40 |
41 | ### File Organization
42 |
43 | - Use `snake_case` for all documentation file names
44 | - Group related content in directories
45 | - Use descriptive directory names that reflect content categories
46 |
47 | ## Monorepo Structure
48 |
49 | The repository uses a monorepo structure to manage:
50 |
51 | - Multiple documentation sites
52 | - Shared tooling and configuration
53 | - Examples and code samples
54 |
55 | ## Required Rules
56 |
57 | The AI agent should read and follow these rules along with the subject rule:
58 |
59 | - **[Naming Conventions](mdc:.cursor/rules/guidelines/naming_conventions.mdc)**: For file and folder naming
60 | - **[Project Overview](mdc:.cursor/rules/project/overview.mdc)**: For repository purpose and goals
61 |
--------------------------------------------------------------------------------
/github-page/src/components/HomepageFeatures/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import styles from './styles.module.css';
4 |
5 | const FeatureList = [
6 | // {
7 | // title: 'Easy to Use',
8 | // Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
9 | // description: (
10 | // <>
11 | // Docusaurus was designed from the ground up to be easily installed and
12 | // used to get your website up and running quickly.
13 | // >
14 | // ),
15 | // },
16 | // {
17 | // title: 'Focus on What Matters',
18 | // Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
19 | // description: (
20 | // <>
21 | // Docusaurus lets you focus on your docs, and we'll do the chores. Go
22 | // ahead and move your docs into the docs directory.
23 | // >
24 | // ),
25 | // },
26 | // {
27 | // title: 'Powered by React',
28 | // Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
29 | // description: (
30 | // <>
31 | // Extend or customize your website layout by reusing React. Docusaurus can
32 | // be extended while reusing the same header and footer.
33 | // >
34 | // ),
35 | // },
36 | ];
37 |
38 | function Feature({ Svg, title, description }) {
39 | return (
40 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/docs/drafts/type-casting-and-coercion.md:
--------------------------------------------------------------------------------
1 | # Type Casting & Coercion
2 |
3 | - Perform type coercion at the beginning of the statement.
4 | - Strings:
5 |
6 | ```typescript
7 | // => this.reviewScore = 9;
8 |
9 | // bad
10 | const totalScore = this.reviewScore + '';
11 |
12 | // good
13 | const totalScore = String(this.reviewScore);
14 | ```
15 |
16 | - Numbers: Use `Number` for type casting and `parseInt` always with a radix for parsing strings. eslint: [`radix`](http://eslint.org/docs/rules/radix)
17 |
18 | ```typescript
19 | const inputValue = '4';
20 |
21 | // bad
22 | const val = new Number(inputValue);
23 |
24 | // bad
25 | const val = +inputValue;
26 |
27 | // bad
28 | const val = inputValue >> 0;
29 |
30 | // bad
31 | const val = parseInt(inputValue);
32 |
33 | // good
34 | const val = Number(inputValue);
35 |
36 | // good
37 | const val = parseInt(inputValue, 10);
38 | ```
39 |
40 | - If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](http://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing.
41 |
42 | ```typescript
43 | // good
44 | /**
45 | * parseInt was the reason my code was slow.
46 | * Bitshifting the String to coerce it to a
47 | * Number made it a lot faster.
48 | */
49 | const val = inputValue >> 0;
50 | ```
51 |
52 | - **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647:
53 |
54 | ```typescript
55 | 2147483647 >> 0 //=> 2147483647
56 | 2147483648 >> 0 //=> -2147483648
57 | 2147483649 >> 0 //=> -2147483647
58 | ```
59 |
60 | - Booleans:
61 |
62 | ```typescript
63 | const age = 0;
64 |
65 | // bad
66 | const hasAge = new Boolean(age);
67 |
68 | // good
69 | const hasAge = Boolean(age);
70 |
71 | // good
72 | const hasAge = !!age;
73 | ```
74 |
--------------------------------------------------------------------------------
/slides/ts.module-resolution.slides.md:
--------------------------------------------------------------------------------
1 | ---
2 | customTheme: "_revealjs/style-basic"
3 | transition: "fade"
4 | logoImg: "_revealjs/uni.png"
5 | enableMenu: false
6 | enableTitleFooter: false
7 | enableChalkboard: false
8 | highlightTheme: vs2015
9 | autoPlayMedia: true
10 | ---
11 |
12 | ## `module` and `moduleResolution`
13 |
14 | ```json
15 | {
16 | "compilerOptions": {
17 | "module": ???,
18 | "moduleResolution": ???
19 | }
20 | }
21 | ```
22 |
23 | ---
24 |
25 | ### What they do
26 |
27 | - `module` specifies how TypeScript emits modules.
28 | - `moduleResolution` specifies how TypeScript resolves modules.
29 |
30 | ---
31 |
32 | ### Possible values
33 |
34 | `module`: `None`, `CommonJS`, `AMD`, `UMD`, `System`, `ES6`, `ES2015`, `ES2020`, `ES2022`, `ESNext`, `Node`, `Node16`, `NodeNext`
35 |
36 | `moduleResolution`: `Classic`, `Node`, `Node16`, `NodeNext`
37 |
38 | ---
39 |
40 | ### `module`
41 |
42 | - `None`, `AMD`, `UMD`, `System`: ignore them {.fragment .fade-left}
43 | - `CommonJS` {.fragment .fade-left}
44 | - `ES6`/`ES2015` {.fragment .fade-left}
45 | - `ES2020`: dynamic `import()`, `import.meta` (NodeJS 14+, 10.4.0+) {.fragment .fade-left}
46 | - `ES2022`: top-level `await` (NodeJS 14.8+) {.fragment .fade-left}
47 | - `ESNext`: latest ECMAScript version {.fragment .fade-left}
48 | - `Node16`: NodeJS native ESM Module support {.fragment .fade-left}
49 | - `NodeNext`: latest Node.js version {.fragment .fade-left}
50 |
51 | ---
52 |
53 | ### `moduleResolution`
54 |
55 | - `Classic`: ignore {.fragment .fade-left}
56 | - `Node` {.fragment .fade-left}
57 | - `Node16`: NodeJS native ESM Module support {.fragment .fade-left}
58 | - `NodeNext`: latest Node.js version {.fragment .fade-left}
59 |
60 | ---
61 |
62 | ### `module`: `ES` or `Node`?
63 |
64 | - How the package is consumed {.fragment .fade-left}
65 | - NodeJS version
66 | - Deno?
67 | - Bundler support
68 | - Your bundler
69 | - Eventually `Node16` {.fragment .fade-left}
70 |
71 | ---
72 |
73 | ### `moduleResolution`: `Node` or `Node16`?
74 |
75 | - Case by case {.fragment .fade-left}
76 | - Can you switch?
77 | - Eventually `Node16` {.fragment .fade-left}
78 | - with `module: ES*`, need dynamic import
79 |
80 |
--------------------------------------------------------------------------------
/.cursor/rules/guidelines/review_rubric.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description: Review rubric and quality checklist for TypeScript Blackbook articles. Use when reviewing or validating content before publication.
3 | globs: "**/*.mdx","**/*.md"
4 | alwaysApply: false
5 | ---
6 |
7 | # ✅ Review Rubric
8 |
9 | An article is "ready" only if ALL items are true:
10 |
11 | ☐ Follows voice and tone guidelines from [Voice and Tone](mdc:.cursor/rules/guidelines/voice_and_tone.mdc)
12 | ☐ Uses the template structure from [Article Template](mdc:.cursor/rules/templates/article_template.mdc)
13 | ☐ Frontmatter includes title and relevant tags
14 | ☐ All recommendations include "Why?" explanations
15 | ☐ Code examples are practical, runnable, and compile-safe
16 | ☐ Links to official TypeScript documentation included where relevant
17 | ☐ Trade-offs and alternatives mentioned when appropriate
18 | ☐ File name follows `snake_case` convention
19 | ☐ Content is concise but comprehensive
20 | ☐ Personal insights or real-world examples included when relevant
21 | ☐ Syntax conventions from [Syntax Conventions](mdc:.cursor/rules/guidelines/syntax_conventions.mdc) are followed
22 | ☐ Code examples use proper syntax highlighting with language tags
23 | ☐ Good/bad examples use ✅/❌ markers when demonstrating antipatterns
24 |
25 | ## Key Principles Checklist
26 |
27 | ☐ **Focus on "why" not just "what"** - Reasoning is explained
28 | ☐ **Be practical** - Real-world applications and benefits shown
29 | ☐ **Acknowledge trade-offs** - Alternatives mentioned when relevant
30 | ☐ **Use examples** - Code examples make concepts concrete
31 | ☐ **Stay current** - TypeScript versions referenced when relevant
32 | ☐ **Be concise** - Respects reader's time while being thorough
33 | ☐ **Show, don't just tell** - Code examples and practical scenarios included
34 |
35 | ## Required Rules
36 |
37 | The AI agent should read and follow these rules along with the subject rule:
38 |
39 | - **[Voice and Tone](mdc:.cursor/rules/guidelines/voice_and_tone.mdc)**: For writing style standards
40 | - **[Syntax Conventions](mdc:.cursor/rules/guidelines/syntax_conventions.mdc)**: For formatting requirements
41 | - **[Article Template](mdc:.cursor/rules/templates/article_template.mdc)**: For structure requirements
42 |
--------------------------------------------------------------------------------
/docs/pages/07-files-and-projects/file-types.md:
--------------------------------------------------------------------------------
1 | # Files
2 |
3 | Out of the box, the TypeScript compiler can understand TypeScript files (`*.ts`) and TypeScript declaration files (aka typings files, `*.d.ts`).
4 | Optionally, it can understand TypeScript React files (`*.tsx`) and JavaScript files (`*.js`)
5 |
6 | But do you know that each of them can also be categorized as either a "script file" or a "module file"?
7 |
8 | ## TypeScript file
9 |
10 | Doesn't need much explanation.
11 | When you name your file with `.ts` extension, your IDE and compiler will treat it as TypeScript file.
12 |
13 | ## TypeScript declaration file
14 |
15 | TypeScript declaration files are used to describe the "typings" of some JavaScript libraries.
16 | They are the files created in [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped/) or Repositories referenced in [`typings` registry](https://github.com/typings/registry).
17 |
18 | They are "ambient" in nature, meaning it does not contain any actual implementation code.
19 |
20 | The formal term for these files is "file with ambient module declaration".
21 | You will see why in a moment.
22 |
23 | ## Script file
24 |
25 | Script files are files that do not have any top level `import`/`export` declaration.
26 | Sometimes they are also called as "global files" because they are global in nature.
27 | For example, the following script file will create a global variable `window.Foo` in browser environment:
28 |
29 | ```ts
30 | namespace Foo {
31 | export const SOMETHING = 1;
32 | };
33 | ```
34 |
35 | Thus TypeScript files or declaration files written in this form could be described as "script file" or "script file with ambient module declaration"
36 |
37 | ## Module file
38 |
39 | Module files are files that has one or more top level `import`/`export` declaration.
40 | Being a module file means its content is not global, and it is align with what a `module` means in ECMAScript.
41 |
42 | Thus TypeScript files or declaration files written in this form could be described as "module file" or "module file with ambient module declaration"
43 |
44 | ## References
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/page/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to [Astro](https://astro.build)
2 |
3 | [](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
4 | [](https://codesandbox.io/s/github/withastro/astro/tree/latest/examples/basics)
5 |
6 | > 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
7 |
8 | 
9 |
10 |
11 | ## 🚀 Project Structure
12 |
13 | Inside of your Astro project, you'll see the following folders and files:
14 |
15 | ```
16 | /
17 | ├── public/
18 | │ └── favicon.svg
19 | ├── src/
20 | │ ├── components/
21 | │ │ └── Card.astro
22 | │ ├── layouts/
23 | │ │ └── Layout.astro
24 | │ └── pages/
25 | │ └── index.astro
26 | └── package.json
27 | ```
28 |
29 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
30 |
31 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
32 |
33 | Any static assets, like images, can be placed in the `public/` directory.
34 |
35 | ## 🧞 Commands
36 |
37 | All commands are run from the root of the project, from a terminal:
38 |
39 | | Command | Action |
40 | | :--------------------- | :------------------------------------------------- |
41 | | `npm install` | Installs dependencies |
42 | | `npm run dev` | Starts local dev server at `localhost:3000` |
43 | | `npm run build` | Build your production site to `./dist/` |
44 | | `npm run preview` | Preview your build locally, before deploying |
45 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
46 | | `npm run astro --help` | Get help using the Astro CLI |
47 |
48 | ## 👀 Want to learn more?
49 |
50 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
51 |
--------------------------------------------------------------------------------
/apps/tutorial/README.md:
--------------------------------------------------------------------------------
1 | # Astro Starter Kit: Minimal
2 |
3 | ```sh
4 | npm create astro@latest -- --template minimal
5 | ```
6 |
7 | [](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
8 | [](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
9 | [](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json)
10 |
11 | > 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
12 |
13 | ## 🚀 Project Structure
14 |
15 | Inside of your Astro project, you'll see the following folders and files:
16 |
17 | ```text
18 | /
19 | ├── public/
20 | ├── src/
21 | │ └── pages/
22 | │ └── index.astro
23 | └── package.json
24 | ```
25 |
26 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
27 |
28 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
29 |
30 | Any static assets, like images, can be placed in the `public/` directory.
31 |
32 | ## 🧞 Commands
33 |
34 | All commands are run from the root of the project, from a terminal:
35 |
36 | | Command | Action |
37 | | :------------------------ | :----------------------------------------------- |
38 | | `npm install` | Installs dependencies |
39 | | `npm run dev` | Starts local dev server at `localhost:4321` |
40 | | `npm run build` | Build your production site to `./dist/` |
41 | | `npm run preview` | Preview your build locally, before deploying |
42 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
43 | | `npm run astro -- --help` | Get help using the Astro CLI |
44 |
45 | ## 👀 Want to learn more?
46 |
47 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
48 |
--------------------------------------------------------------------------------
/apps/tutorial/src/pages/about.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Navigation from '../components/navigation.astro'
3 | import Footer from '../components/footer.astro'
4 | import "../styles/global.css";
5 | import Header from '../components/header.astro'
6 |
7 | const pageTitle = 'About Me'
8 | const identity = {
9 | firstName: 'Sarah',
10 | country: 'Canada',
11 | occupation: 'Technical Writer',
12 | hobbies: ['photography', 'birdwatching', 'baseball']
13 | }
14 |
15 | const skills = ['HTML', 'CSS', 'JavaScript', 'React', 'Astro', 'Writing Docs']
16 |
17 | const happy = true
18 | const finished = false
19 | const goal = 3
20 | const skillColor = "navy";
21 | ---
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {pageTitle}
30 |
37 |
38 |
39 |
40 |
41 |
{pageTitle}
42 |
... and my new Astro site!
43 |
44 |
45 | I am working through Astro's introductory tutorial. This is the second page on my website, and it's the
46 | first one I built myself!
47 |
48 |
49 |
50 | This site will update as I complete more of the tutorial, so keep checking back and see how my journey
51 | is going!
52 |
53 |
Here are a few facts about me:
54 |
55 |
My name is {identity.firstName}.
56 |
I live in {identity.country} and I work as a {identity.occupation}.
60 | Two of my hobbies are: {identity.hobbies[0]} and {identity.hobbies[1]}
61 |
62 | )
63 | }
64 |
65 |
My skills are:
66 |
67 | {skills.map(skill =>
{skill}
)}
68 |
69 | {happy &&
I am happy to be learning Astro!
}
70 |
71 | {finished &&
I finished this tutorial!
}
72 |
73 | {goal === 3 ?
My goal is to finish in 3 days.
:
My goal is not 3 days.
}
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/async-await.md:
--------------------------------------------------------------------------------
1 | # Async await
2 |
3 | `async/await` is a JavaScript syntactic sugar combining `Promise` and `generator` to create a powerful asynchronous mechanism.
4 |
5 | You have to declare a function to be `async` before you can use `await` within the function.
6 |
7 | ```ts
8 | // error
9 | function foo() {
10 | return await doSomeAsyncWork()
11 | }
12 | ```
13 |
14 | when you add `await` to a statement, it will check if the result of the statement is an `Promise`.
15 | If it is, then the code will be blocked and wait for the promise to resolve.
16 |
17 | -
18 | -
19 |
20 | ## Concurrent execution
21 |
22 | Using `async/await` **may** negatively impact your code performance.
23 |
24 | ```ts
25 | // bad, this function will not return until `p1` and `p2` are resolved
26 | async function doMultipleThings() {
27 | const p1 = doSomeAsyncWork()
28 | const p2 = doSomeOtherAsyncWork()
29 | return await p1 + await p2 + 1
30 | }
31 |
32 | // good, no blocking
33 | function doMultipleThings() {
34 | const p1 = doSomeAsyncWork()
35 | const p2 = doSomeOtherAsyncWork()
36 | return Promise.all([p1, p2]).then(([x, y]) => x + y + 1)
37 | }
38 | ```
39 |
40 | > Why?
41 |
42 | `await` will block the call until the `Promise` is resolved,
43 | it may slow down your code.
44 |
45 | ## Return await
46 |
47 | **Should** void `return await`.
48 |
49 | ```ts
50 | // bad
51 | async function foo() {
52 | return await doSomethingAsync()
53 | }
54 |
55 | // good
56 | async function foo() {
57 | return doSomethingAsync()
58 | }
59 |
60 | // also good
61 | async function tryDo() {
62 | try {
63 | return await doSomethingAsync()
64 | }
65 | catch (e) {
66 | // process the rejected value
67 | }
68 | }
69 | ```
70 |
71 | > Why?
72 |
73 | `return await` will cause your code to wait for the promise instead of propagating the promise to the caller.
74 | This is a common mistake made by novice and most likely is a mistake.
75 |
76 | > Why Not?
77 |
78 | Sometimes you do want to wait for the promise.
79 | For example, you want to `try-catch` the rejected promise.
80 |
--------------------------------------------------------------------------------
/docs/pages/02-javascript-syntax/this.md:
--------------------------------------------------------------------------------
1 | # this
2 |
3 | > A property of an execution context (global, function or eval) that, in non–strict mode, is always a reference to an object and in strict mode can be any value.
4 |
5 | -
6 |
7 | `this` is a messy topic, pun intented.
8 |
9 | It is one of the main sources of confusion, frustration, and sleepless nights if you are new to JavaScript.
10 |
11 | ## When to use
12 |
13 | You **should not** use `this`, except inside class.
14 |
15 | ```ts
16 | // bad
17 | function play() { this.energy-- }
18 |
19 | // bad
20 | const gamer = {
21 | energy: 100,
22 | play() { this.energy-- }
23 | }
24 |
25 | // good
26 | class Gamer {
27 | energy = 100
28 | play() { this.energy-- }
29 | }
30 |
31 | // good
32 | function createGamer() {
33 | let energy = 100
34 | return { play() { energy-- } }
35 | }
36 | ```
37 |
38 | > Why?
39 |
40 | Except arrow function, which do not have its own `this`,
41 | the value of `this` is determined by how a function is called (runtime binding).
42 |
43 | This mean everytime you use `this`,
44 | you are making an assumption on how the user is going to use your code.
45 |
46 | This assumption is communicated to the user is very implicit ways:
47 |
48 | - should this function be used as a mixin? (first function)
49 | - is this object a coherent instance? (second object)
50 |
51 | For class, since it is a build in syntax,
52 | it provides a sufficient clue to the user that you should not pick out the function and use it separately.
53 |
54 | ```ts
55 | // this will almost guaranteed to fail
56 | const instance = new SomeClass()
57 | const fn = instance.doSomething
58 | fn()
59 | ```
60 |
61 | And since `this` is the only way in a class to access its own property,
62 | it is ok to use it.
63 |
64 | Note that the first example does not work when you have `noImplicitThis` turned on.
65 |
66 | With `noImplicitThis`, you need to define the `this` type:
67 |
68 | ```ts
69 | // good
70 | function play(this: Gamer) { this.energy-- }
71 | ```
72 |
73 | With that information, now it is safe to use the function as mixins.
74 |
75 | ```ts
76 |
77 | let deadMeat = { play }
78 |
79 | // error, `deadMeat` does not have `energy`.
80 | deadMeat.play()
81 | ```
82 |
83 | ## Alternatives
84 |
--------------------------------------------------------------------------------
/docs/pages/typings/namespaces-and-modules.md:
--------------------------------------------------------------------------------
1 | # Namespace and Module
2 |
3 | When declaring a module (or namespace), there are two options:
4 |
5 | - declaration wrapped in `declare {namespace,module} {`, or
6 | - top-level declaration
7 |
8 | > Top-level declarations in a source file with no top-level import or export declarations belong to the global namespace.
9 | > Top-level declarations in a source file with one or more top-level import or export declarations belong to the module represented by that source file. ([link](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#23-declarations), need to scroll down a bit)
10 |
11 | ## Namespace (Internal Module)
12 |
13 | - Use `declare namespace X {` syntax.
14 | - Avoid `declare module X {` syntax.
15 |
16 | > Why? `declare namespace X {` will fixate the namespace to the name `X`, which is what you want.
17 |
18 | tslint [`no-internal-module`](tslint.md/no-internal-module-native)
19 |
20 | ```ts
21 | // bad
22 | declare module Chai {
23 | // stuff...
24 | }
25 |
26 | // good
27 | declare namespace Chai {
28 | // stuff...
29 | }
30 | ```
31 |
32 | ## Module (External Module)
33 |
34 | - Do not wrap typings in `declare module "X" {`. Expose as **top-level declaration**
35 |
36 | > Why? `declare module "X" {` will cause name conflict if consumer use two different version of the same library.
37 |
38 | ```ts
39 | // bad
40 | declare module "X" {
41 | export interface A {
42 | // stuff...
43 | };
44 | }
45 |
46 | // good
47 | export interface A {
48 | // stuff...
49 | };
50 | ```
51 |
52 | ## Note
53 |
54 | Prior to TypeScript 1.5, there are two types of modules:
55 |
56 | - Internal module (`declare module X {`)
57 | - External module (`declare module "X" {`)
58 |
59 | In TypeScript 1.5, the term and keyword `namespace` is introduced.
60 | The nomenclature has changed.
61 |
62 | - Internal module -> namespace
63 | - External module -> module
64 |
65 | The `declare module X {` syntax exists for backward compatibility.
66 |
67 | ## Reference
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/docs/drafts/blocks.md:
--------------------------------------------------------------------------------
1 | # Blocks
2 |
3 | ## Single-line blocks
4 |
5 | - Avoid single-line for now
6 |
7 | > Why? Currently tslint does not support it well
8 |
9 | ## Multi-line blocks
10 |
11 | - Use braces with all multi-line blocks.
12 |
13 | tslint: [`curly`](tslint.md#curly-native)
14 |
15 | ```typescript
16 | // bad
17 | if (test)
18 | return false;
19 |
20 | // good
21 | if (test) return false;
22 |
23 | // good
24 | if (test) {
25 | return false;
26 | }
27 |
28 | // bad
29 | function foo() { return false; }
30 |
31 | // good
32 | function bar() {
33 | return false;
34 | }
35 | ```
36 |
37 | ## `else` clause placement
38 |
39 | - If you're using multi-line blocks with `if` and `else`, put `else` on the next line after your `if` block's closing brace.
40 |
41 | > Why? Code folding would collapse all cases into a single line, making it hard to read.
42 |
43 | ```typescript
44 | // bad
45 | if (test) {
46 | thing1();
47 | thing2();
48 | } else {
49 | thing3();
50 | }
51 |
52 | // good
53 | if (test) {
54 | thing1();
55 | thing2();
56 | }
57 | else {
58 | thing3();
59 | }
60 | ```
61 |
62 | ### Switch case
63 |
64 | - Use braces to create blocks in `case` and `default` clauses that contain lexical declarations (e.g. `let`, `const`, `function`, and `class`).
65 |
66 | > Why? Lexical declarations are visible in the entire `switch` block but only get initialized when assigned, which only happens when its `case` is reached. This causes problems when multiple `case` clauses attempt to define the same thing.
67 |
68 | eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html).
69 |
70 | ```typescript
71 | // bad
72 | switch (foo) {
73 | case 1:
74 | let x = 1;
75 | break;
76 | case 2:
77 | const y = 2;
78 | break;
79 | case 3:
80 | function f() {}
81 | break;
82 | default:
83 | class C {}
84 | }
85 |
86 | // good
87 | switch (foo) {
88 | case 1: {
89 | let x = 1;
90 | break;
91 | }
92 | case 2: {
93 | const y = 2;
94 | break;
95 | }
96 | case 3: {
97 | function f() {}
98 | break;
99 | }
100 | case 4:
101 | bar();
102 | break;
103 | default: {
104 | class C {}
105 | }
106 | }
107 | ```
108 |
--------------------------------------------------------------------------------
/github-page/blog/2022-05-15-eslint-plugin-peer-deps.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: 2022-eslint-plugin-peer-deps
3 | title: "@typescript-eslint/eslint-plugin should be a peer dependency"
4 | authors: [unional]
5 | tags: [typescript, eslint, "@typescript-eslint"]
6 | ---
7 |
8 | # The Problem
9 |
10 | Recently I run into this problem:
11 |
12 | ```sh
13 | ESLint couldn't determine the plugin "@typescript-eslint" uniquely.
14 |
15 | - ...\node_modules\@typescript-eslint\eslint-plugin\dist\index.js (loaded in ".eslintrc.json")
16 | - ...\node_modules\eslint-config-react-app\node_modules\@typescript-eslint\eslint-plugin\dist\index.js (loaded in ".eslintrc.json » eslint-config-react-app#overrides[0]")
17 | ```
18 |
19 | It is caused by [eslint-config-react-app] using [@typescript-eslint/eslint-plugin] as a `dependency` instead of a `peer dependency`.
20 |
21 | Here is the [GitHub issue] in [eslint-config-react-app] (also [here][#11828]).
22 |
23 | ## Plugin and dependency
24 |
25 | The problem is when one plugin uses another plugin,
26 | it should always declare the dependency as a `peer dependency`.
27 |
28 | The reason is simple.
29 |
30 | The host application (ESLint in this case) controls and loads its plugins.
31 | If the host application is not designed to support loading multiple versions of the same plugin at the same time,
32 | which most of them don't, then the result is an undefined behavior.
33 |
34 | That's why ESLint plainly detects and disallows it.
35 |
36 | It also mentioned it in [its doc].
37 |
38 | Yes, that means the consuming repository needs to add the dependency themselves.
39 | Any version incompatibility in the dependency graph would lead to the [doppelgängers] problem.
40 |
41 | That is an inherited problem of NodeJS resolution algorithm, and is a topic for another day.
42 |
43 | Happy coding, 🧑💻
44 |
45 | [@typescript-eslint/eslint-plugin]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin
46 | [#11828]: https://github.com/facebook/create-react-app/issues/11828
47 | [doppelgängers]: https://rushjs.io/pages/advanced/npm_doppelgangers/
48 | [eslint-config-react-app]: https://github.com/facebook/create-react-app/tree/main/packages/eslint-config-react-app
49 | [GitHub issue]: https://github.com/facebook/create-react-app/issues/12400
50 | [its doc]: https://eslint.org/docs/developer-guide/shareable-configs#publishing-a-shareable-config
51 |
--------------------------------------------------------------------------------
/apps/starlight/src/content/blogs/2022-05-15-eslint-plugin-peer-deps.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: 2022-eslint-plugin-peer-deps
3 | title: "@typescript-eslint/eslint-plugin should be a peer dependency"
4 | pubDate: 2022-05-15
5 | tags: [typescript, eslint, "@typescript-eslint"]
6 | ---
7 |
8 | # The Problem
9 |
10 | Recently I run into this problem:
11 |
12 | ```sh
13 | ESLint couldn't determine the plugin "@typescript-eslint" uniquely.
14 |
15 | - ...\node_modules\@typescript-eslint\eslint-plugin\dist\index.js (loaded in ".eslintrc.json")
16 | - ...\node_modules\eslint-config-react-app\node_modules\@typescript-eslint\eslint-plugin\dist\index.js (loaded in ".eslintrc.json » eslint-config-react-app#overrides[0]")
17 | ```
18 |
19 | It is caused by [eslint-config-react-app] using [@typescript-eslint/eslint-plugin] as a `dependency` instead of a `peer dependency`.
20 |
21 | Here is the [GitHub issue] in [eslint-config-react-app] (also [here][#11828]).
22 |
23 | ## Plugin and dependency
24 |
25 | The problem is when one plugin uses another plugin,
26 | it should always declare the dependency as a `peer dependency`.
27 |
28 | The reason is simple.
29 |
30 | The host application (ESLint in this case) controls and loads its plugins.
31 | If the host application is not designed to support loading multiple versions of the same plugin at the same time,
32 | which most of them don't, then the result is an undefined behavior.
33 |
34 | That's why ESLint plainly detects and disallows it.
35 |
36 | It also mentioned it in [its doc].
37 |
38 | Yes, that means the consuming repository needs to add the dependency themselves.
39 | Any version incompatibility in the dependency graph would lead to the [doppelgängers] problem.
40 |
41 | That is an inherited problem of NodeJS resolution algorithm, and is a topic for another day.
42 |
43 | Happy coding, 🧑💻
44 |
45 | [@typescript-eslint/eslint-plugin]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin
46 | [#11828]: https://github.com/facebook/create-react-app/issues/11828
47 | [doppelgängers]: https://rushjs.io/pages/advanced/npm_doppelgangers/
48 | [eslint-config-react-app]: https://github.com/facebook/create-react-app/tree/main/packages/eslint-config-react-app
49 | [GitHub issue]: https://github.com/facebook/create-react-app/issues/12400
50 | [its doc]: https://eslint.org/docs/developer-guide/shareable-configs#publishing-a-shareable-config
51 |
--------------------------------------------------------------------------------
/docs/drafts/strings.md:
--------------------------------------------------------------------------------
1 | # Strings
2 |
3 | ## Quotes
4 |
5 | - Use single quotes `''` for strings. eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks)
6 |
7 | ```typescript
8 | // bad
9 | const name = "Capt. Janeway";
10 |
11 | // good
12 | const name = 'Capt. Janeway';
13 | ```
14 |
15 | ## Long strings
16 |
17 | - Strings that cause the line to go over 100 characters should be written across multiple lines using string concatenation.
18 | - Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40).
19 |
20 | ```typescript
21 | // bad
22 | const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
23 |
24 | // bad
25 | const errorMessage = 'This is a super long error that was thrown because \
26 | of Batman. When you stop to think about how Batman had anything to do \
27 | with this, you would get nowhere \
28 | fast.';
29 |
30 | // good
31 | const errorMessage = 'This is a super long error that was thrown because ' +
32 | 'of Batman. When you stop to think about how Batman had anything to do ' +
33 | 'with this, you would get nowhere fast.';
34 | ```
35 |
36 | ## Template literals
37 |
38 |
39 |
40 | - When programmatically building up strings, use template strings instead of concatenation. eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) jscs: [`requireTemplateStrings`](http://jscs.info/rule/requireTemplateStrings)
41 |
42 | > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
43 |
44 | ```typescript
45 | // bad
46 | function sayHi(name) {
47 | return 'How are you, ' + name + '?';
48 | }
49 |
50 | // bad
51 | function sayHi(name) {
52 | return ['How are you, ', name, '?'].join();
53 | }
54 |
55 | // bad
56 | function sayHi(name) {
57 | return `How are you, ${ name }?`;
58 | }
59 |
60 | // good
61 | function sayHi(name) {
62 | return `How are you, ${name}?`;
63 | }
64 | ```
65 |
66 | ## Eval()
67 |
68 | - Never use `eval()` on a string, it opens too many vulnerabilities.
69 |
--------------------------------------------------------------------------------
/.cursor/rules/project/overview.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description: Project overview and core principles for TypeScript Blackbook. Use when understanding the repository's purpose, audience, and fundamental requirements.
3 | globs: "**/*"
4 | alwaysApply: true
5 | ---
6 |
7 | # 📚 Project Overview (TypeScript Blackbook)
8 |
9 | This repository documents real-world TypeScript techniques and best practices for professional developers.
10 |
11 | **Primary audience**: mid- to senior-level TypeScript developers
12 |
13 | **Desired outcome**: readers can understand TypeScript deeply and apply patterns effectively in their projects.
14 |
15 | ## Core Requirements
16 |
17 | Cursor-AI MUST:
18 |
19 | - **Treat every code example as production-ready** - compile-safe, type-checked, following repository conventions
20 | - **Avoid pseudo-code or incomplete examples** - always use valid TypeScript that demonstrates the concept
21 | - **Cite internal file paths** - reference existing code patterns from the repository when relevant
22 | - **Explain the "why"** - not just the "what", include reasoning behind recommendations
23 | - **Include trade-offs and alternatives** - discuss different approaches when relevant
24 | - **Reference official documentation** - include TypeScript documentation links and version numbers when applicable
25 |
26 | ## Design Philosophy
27 |
28 | This book focuses on *how to get the most out of TypeScript with minimal effort*.
29 |
30 | To achieve that goal, following some coding styles is a good place to start. However, just following *what to do* can only go so far. You need to know:
31 |
32 | - **Why** should you write code in a certain way
33 | - **What** are the **trade-offs** you are making
34 | - The **design** and **limitations** of the language itself
35 |
36 | This knowledge provides the right **mindset** and **approach** to solve problems effectively.
37 |
38 | ## Required Rules
39 |
40 | The AI agent should read and follow these rules along with the subject rule:
41 |
42 | - **[Voice and Tone Guidelines](mdc:.cursor/rules/guidelines/voice_and_tone.mdc)**: For writing style and persona
43 | - **[Syntax Conventions](mdc:.cursor/rules/guidelines/syntax_conventions.mdc)**: For markdown and code formatting
44 | - **[Article Template](mdc:.cursor/rules/templates/article_template.mdc)**: For article structure and organization
45 | - **[Review Rubric](mdc:.cursor/rules/guidelines/review_rubric.mdc)**: For quality assurance checklist
46 |
--------------------------------------------------------------------------------