├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml ├── pull_request_template.md └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── misc ├── typescript-logo-100.png ├── typescript-logo-30.png ├── typescript-logo-40.png ├── typescript-logo-50.png ├── vscode-playwright-test.gif └── vscode-vitest-runner.gif └── website ├── .prettierignore ├── .prettierrc ├── README.md ├── babel.config.js ├── docusaurus.config.ts ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── src ├── components │ ├── GithubStats.tsx │ ├── MainTitle.tsx │ ├── Note.tsx │ ├── Rule.tsx │ ├── TableOfContents.tsx │ └── index.ts ├── hooks │ ├── index.ts │ └── useDocumentTitle.ts ├── pages │ └── index.mdx ├── styles │ └── global.css ├── theme │ └── NavbarItem │ │ └── ComponentTypes.tsx └── utils │ └── cn.ts ├── static ├── .nojekyll └── img │ ├── favicon.ico │ ├── typescript-card.png │ └── typescript-logo-40.png └── tsconfig.json /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 'New issue' 2 | description: 'File an issue' 3 | body: 4 | - type: 'textarea' 5 | id: 'description' 6 | attributes: 7 | label: 'Description' 8 | description: 'Description of an issue.' 9 | placeholder: | 10 | Bug description 11 | validations: 12 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/mkosir/typescript-style-guide/discussions 5 | about: Ask questions and discuss topics with other community members 6 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### 🚀 Pull Request Template 2 | 3 | **Description** 4 | 5 | This PR adds new convention, best practice approach, translation, fixes a typo, ... 6 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | env: 9 | NODE_VERSION: "24.x" 10 | CACHE_PATHS_BUILD: | 11 | ./website/build 12 | CACHE_KEY_BUILD: build-${{ github.sha }}-${{ github.ref_type }} 13 | 14 | defaults: 15 | run: 16 | working-directory: ./website 17 | 18 | jobs: 19 | build: 20 | name: Build 🏗️ 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - name: Checkout 🛎️ 25 | uses: actions/checkout@v4 26 | 27 | - name: Setup Node.js ⚙️ 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: ${{ env.NODE_VERSION }} 31 | cache: "npm" 32 | cache-dependency-path: "./website" 33 | 34 | - name: Install dependencies 🔧 35 | run: npm ci 36 | 37 | - name: Build 🏗️ 38 | run: npm run build 39 | 40 | - name: Cache build ⚡ 41 | id: cache_build 42 | uses: actions/cache@v4 43 | with: 44 | path: ${{ env.CACHE_PATHS_BUILD }} 45 | key: ${{ env.CACHE_KEY_BUILD }} 46 | 47 | lint: 48 | name: Lint ✅ 49 | runs-on: ubuntu-latest 50 | 51 | steps: 52 | - name: Checkout 🛎️ 53 | uses: actions/checkout@v4 54 | 55 | - name: Setup Node.js ⚙️ 56 | uses: actions/setup-node@v4 57 | with: 58 | node-version: ${{ env.NODE_VERSION }} 59 | cache: "npm" 60 | cache-dependency-path: "./website" 61 | 62 | - name: Install dependencies 🔧 63 | run: npm ci 64 | 65 | - name: Lint ✅ 66 | run: npm run lint 67 | 68 | tsc: 69 | name: TypeScript Compiler 🔎 70 | runs-on: ubuntu-latest 71 | 72 | steps: 73 | - name: Checkout 🛎️ 74 | uses: actions/checkout@v4 75 | 76 | - name: Setup Node.js ⚙️ 77 | uses: actions/setup-node@v4 78 | with: 79 | node-version: ${{ env.NODE_VERSION }} 80 | cache: "npm" 81 | cache-dependency-path: "./website" 82 | 83 | - name: Install dependencies 🔧 84 | run: npm ci 85 | 86 | - name: TypeScript Compiler 🔎 87 | run: npm run tsc 88 | 89 | deploy: 90 | name: Deploy 🚀 91 | needs: [build, lint, tsc] 92 | runs-on: ubuntu-latest 93 | 94 | steps: 95 | - name: Checkout 🛎️ 96 | uses: actions/checkout@v4 97 | 98 | - uses: actions/cache@v4 99 | with: 100 | path: ${{ env.CACHE_PATHS_BUILD }} 101 | key: ${{ env.CACHE_KEY_BUILD }} 102 | 103 | - name: Deploy 🚀 104 | uses: JamesIves/github-pages-deploy-action@v4 105 | with: 106 | branch: gh-pages 107 | folder: website/build 108 | clean: true 109 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | compiled 5 | build 6 | 7 | .DS_Store 8 | .vscode 9 | .idea 10 | 11 | npm-debug.log* 12 | 13 | .env 14 | 15 | *.tsbuildinfo 16 | 17 | .docusaurus 18 | .cache-loader -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 mkosir 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

TypeScript Style Guide

2 | 3 |

TypeScript Style Guide provides a concise set of conventions and best practices for creating consistent, maintainable code.

4 | 5 | ### Go to 👉 [TypeScript Style Guide](https://mkosir.github.io/typescript-style-guide/) 6 | 7 | ### Why 8 | 9 | - As project grow in size and complexity, maintaining code quality and ensuring consistent practices become increasingly challenging. 10 | - Defining and following a standard approach to writing TypeScript applications leads to a consistent codebase and faster development cycles. 11 | - No need to discuss code styles during code reviews. 12 | - Saves team time and energy. 13 | 14 | ### Contributing 15 | 16 | All contributions are welcome! Open a [PR](https://github.com/mkosir/typescript-style-guide/blob/main/.github/pull_request_template.md), [issue](https://github.com/mkosir/typescript-style-guide/issues/new/choose) or [discussion](https://github.com/mkosir/typescript-style-guide/discussions/new/choose). 17 | -------------------------------------------------------------------------------- /misc/typescript-logo-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkosir/typescript-style-guide/e52658ae1fa7f8b9b993019f9b3d87af2409c5a0/misc/typescript-logo-100.png -------------------------------------------------------------------------------- /misc/typescript-logo-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkosir/typescript-style-guide/e52658ae1fa7f8b9b993019f9b3d87af2409c5a0/misc/typescript-logo-30.png -------------------------------------------------------------------------------- /misc/typescript-logo-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkosir/typescript-style-guide/e52658ae1fa7f8b9b993019f9b3d87af2409c5a0/misc/typescript-logo-40.png -------------------------------------------------------------------------------- /misc/typescript-logo-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkosir/typescript-style-guide/e52658ae1fa7f8b9b993019f9b3d87af2409c5a0/misc/typescript-logo-50.png -------------------------------------------------------------------------------- /misc/vscode-playwright-test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkosir/typescript-style-guide/e52658ae1fa7f8b9b993019f9b3d87af2409c5a0/misc/vscode-playwright-test.gif -------------------------------------------------------------------------------- /misc/vscode-vitest-runner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkosir/typescript-style-guide/e52658ae1fa7f8b9b993019f9b3d87af2409c5a0/misc/vscode-vitest-runner.gif -------------------------------------------------------------------------------- /website/.prettierignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | build 4 | compiled 5 | .docusaurus -------------------------------------------------------------------------------- /website/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "plugins": ["prettier-plugin-tailwindcss"] 6 | } 7 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 3](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ npm i 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ npm run start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | module.exports = { 3 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 4 | }; 5 | -------------------------------------------------------------------------------- /website/docusaurus.config.ts: -------------------------------------------------------------------------------- 1 | import type * as Preset from '@docusaurus/preset-classic'; 2 | import type { Config } from '@docusaurus/types'; 3 | import { themes } from 'prism-react-renderer'; 4 | 5 | const config: Config = { 6 | title: ' ', 7 | tagline: 'TypeScript Style Guide', 8 | favicon: 'img/favicon.ico', 9 | 10 | url: 'https://mkosir.github.io', 11 | // Set the // pathname under which your site is served 12 | // For GitHub pages deployment, it is often '//' 13 | baseUrl: '/typescript-style-guide', 14 | 15 | // GitHub pages deployment config. 16 | organizationName: 'mkosir', 17 | projectName: 'typescript-style-guide', 18 | 19 | trailingSlash: false, 20 | 21 | onBrokenLinks: 'throw', 22 | onBrokenMarkdownLinks: 'warn', 23 | 24 | // Even if you don't use internalization, you can use this field to set useful 25 | // metadata like html lang. For example, if your site is Chinese, you may want 26 | // to replace "en" with "zh-Hans". 27 | i18n: { 28 | defaultLocale: 'en', 29 | locales: ['en'], 30 | }, 31 | 32 | presets: [ 33 | [ 34 | 'classic', 35 | { 36 | docs: false, 37 | blog: false, 38 | theme: { 39 | customCss: './src/styles/global.css', 40 | }, 41 | } satisfies Preset.Options, 42 | ], 43 | ], 44 | 45 | plugins: [ 46 | function tailwindPlugin() { 47 | return { 48 | name: 'tailwind-plugin', 49 | configurePostCss(postcssOptions) { 50 | // eslint-disable-next-line @typescript-eslint/no-require-imports 51 | postcssOptions.plugins.push(require('@tailwindcss/postcss')); 52 | return postcssOptions; 53 | }, 54 | }; 55 | }, 56 | ], 57 | 58 | themeConfig: { 59 | colorMode: { 60 | defaultMode: 'dark', 61 | respectPrefersColorScheme: false, 62 | }, 63 | image: 'img/typescript-card.png', 64 | navbar: { 65 | title: 'TypeScript Style Guide', 66 | logo: { 67 | alt: 'logo', 68 | src: 'img/typescript-logo-40.png', 69 | height: 22, 70 | width: 22, 71 | style: { height: 'auto', marginTop: 5 }, 72 | }, 73 | items: [ 74 | { 75 | type: 'custom-GithubStats', 76 | position: 'right', 77 | }, 78 | { 79 | href: 'https://github.com/mkosir/typescript-style-guide', 80 | label: 'GitHub', 81 | position: 'right', 82 | }, 83 | ], 84 | }, 85 | footer: { 86 | style: 'dark', 87 | copyright: `TypeScript Style Guide. Built with Docusaurus.`, 88 | }, 89 | prism: { 90 | theme: themes.github, 91 | darkTheme: themes.okaidia, 92 | }, 93 | algolia: { 94 | // Public application ID provided by Algolia 95 | appId: '3CBFIG0U2G', 96 | // Public API key 97 | apiKey: 'c32aa1ae163b2d28ccd2fe42ee3b73be', 98 | indexName: 'typescript-style-guide', 99 | // Optional: see doc section below 100 | contextualSearch: true, 101 | // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. 102 | externalUrlRegex: 'external\\.com|domain\\.com', 103 | // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs 104 | replaceSearchResultPathname: { 105 | from: '/docs/', // or as RegExp: /\/docs\// 106 | to: '/', 107 | }, 108 | // Optional: Algolia search parameters 109 | searchParameters: {}, 110 | // Optional: path for search page that enabled by default (`false` to disable it) 111 | searchPagePath: 'search', 112 | }, 113 | } satisfies Preset.ThemeConfig, 114 | }; 115 | 116 | // eslint-disable-next-line import/no-default-export 117 | export default config; 118 | -------------------------------------------------------------------------------- /website/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js'; 2 | import eslintConfigPrettier from 'eslint-config-prettier'; 3 | import eslintPluginImport from 'eslint-plugin-import'; 4 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 5 | import eslintPluginReact from 'eslint-plugin-react'; 6 | import eslintPluginReactHooks from 'eslint-plugin-react-hooks'; 7 | import eslintPluginJsxA11y from 'eslint-plugin-jsx-a11y'; 8 | // eslint-disable-next-line import/no-unresolved 9 | import tseslint from 'typescript-eslint'; 10 | 11 | export default tseslint.config( 12 | eslint.configs.recommended, 13 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access 14 | eslintPluginImport.flatConfigs.recommended, 15 | ...tseslint.configs.strictTypeChecked, 16 | ...tseslint.configs.stylisticTypeChecked, 17 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access 18 | eslintPluginJsxA11y.flatConfigs.recommended, 19 | eslintPluginReact.configs.flat.recommended, 20 | eslintPluginPrettierRecommended, 21 | eslintConfigPrettier, 22 | { 23 | ignores: ['!.*', 'node_modules', 'dist', 'compiled', 'build', '.docusaurus'], 24 | }, 25 | { 26 | languageOptions: { 27 | parserOptions: { 28 | projectService: true, 29 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 30 | tsconfigRootDir: import.meta.name, 31 | }, 32 | }, 33 | settings: { 34 | react: { version: 'detect' }, 35 | }, 36 | }, 37 | { 38 | files: ['**/*.{js,ts,tsx}'], 39 | 40 | plugins: { 41 | 'react-hooks': eslintPluginReactHooks, 42 | }, 43 | 44 | rules: { 45 | ...eslintPluginReactHooks.configs.recommended.rules, 46 | 47 | 'react/jsx-sort-props': ['error', { callbacksLast: true, shorthandFirst: true }], 48 | 'react/react-in-jsx-scope': 'off', 49 | 'react/hook-use-state': 'error', 50 | 51 | 'prefer-template': 'error', 52 | 'no-nested-ternary': 'error', 53 | 'no-unneeded-ternary': 'error', 54 | 55 | '@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }], 56 | '@typescript-eslint/consistent-type-definitions': ['error', 'type'], 57 | '@typescript-eslint/array-type': ['error', { default: 'generic' }], 58 | '@typescript-eslint/prefer-nullish-coalescing': 'error', 59 | '@typescript-eslint/no-unnecessary-condition': 'error', 60 | '@typescript-eslint/no-confusing-void-expression': ['error', { ignoreArrowShorthand: true }], 61 | '@typescript-eslint/restrict-plus-operands': 'off', 62 | '@typescript-eslint/consistent-type-imports': 'error', 63 | '@typescript-eslint/naming-convention': [ 64 | 'error', 65 | { 66 | selector: 'typeAlias', 67 | format: ['PascalCase'], 68 | }, 69 | { 70 | selector: 'variable', 71 | types: ['boolean'], 72 | format: ['PascalCase'], 73 | prefix: ['is', 'are', 'should', 'has', 'can', 'did', 'will'], 74 | }, 75 | { 76 | // Generic type parameter must start with letter T, followed by any uppercase letter. 77 | selector: 'typeParameter', 78 | format: ['PascalCase'], 79 | custom: { regex: '^T[A-Z]', match: true }, 80 | }, 81 | ], 82 | 83 | 'import/no-default-export': 'error', 84 | 'import/order': [ 85 | 'error', 86 | { 87 | groups: ['builtin', 'external', 'internal', 'parent', 'sibling'], 88 | 'newlines-between': 'always', 89 | alphabetize: { 90 | order: 'asc', 91 | caseInsensitive: true, 92 | }, 93 | }, 94 | ], 95 | 'import/no-unresolved': 'off', 96 | }, 97 | }, 98 | ); 99 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-style-guide-website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "clear": "docusaurus clear", 11 | "serve": "docusaurus serve", 12 | "write-translations": "docusaurus write-translations", 13 | "write-heading-ids": "docusaurus write-heading-ids", 14 | "lint": "eslint --report-unused-disable-directives --max-warnings 0 .", 15 | "lint-fix": "eslint --fix .", 16 | "tsc": "tsc -p tsconfig.json", 17 | "format-lint": "prettier --config .prettierrc --check --ignore-unknown .", 18 | "format-fix": "prettier --config .prettierrc --write --ignore-unknown -l .", 19 | "clean": "rm -rf node_modules coverage dist compiled build .docusaurus" 20 | }, 21 | "dependencies": { 22 | "@docusaurus/core": "3.8.0", 23 | "@docusaurus/preset-classic": "3.8.0", 24 | "@mdx-js/react": "3.1.0", 25 | "clsx": "2.1.1", 26 | "prism-react-renderer": "2.4.1", 27 | "react": "19.1.0", 28 | "react-animate-height": "3.2.3", 29 | "react-dom": "19.1.0", 30 | "react-parallax-tilt": "1.7.296", 31 | "tailwind-merge": "3.3.0" 32 | }, 33 | "devDependencies": { 34 | "@docusaurus/module-type-aliases": "3.8.0", 35 | "@docusaurus/tsconfig": "3.8.0", 36 | "@eslint/js": "9.27.0", 37 | "@tailwindcss/postcss": "4.1.7", 38 | "@types/react": "19.1.6", 39 | "eslint": "9.27.0", 40 | "eslint-config-prettier": "10.1.5", 41 | "eslint-plugin-import": "2.31.0", 42 | "eslint-plugin-jsx-a11y": "6.10.2", 43 | "eslint-plugin-prettier": "5.4.0", 44 | "eslint-plugin-react": "7.37.5", 45 | "eslint-plugin-react-hooks": "5.2.0", 46 | "prettier": "3.5.3", 47 | "prettier-plugin-tailwindcss": "0.6.11", 48 | "tailwindcss": "4.1.7", 49 | "typescript": "5.8.3", 50 | "typescript-eslint": "8.33.0" 51 | }, 52 | "browserslist": { 53 | "production": [ 54 | ">0.5%", 55 | "not dead", 56 | "not op_mini all" 57 | ], 58 | "development": [ 59 | "last 1 chrome version", 60 | "last 1 firefox version", 61 | "last 1 safari version" 62 | ] 63 | }, 64 | "engines": { 65 | "node": ">=24.1.0" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /website/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /website/src/components/GithubStats.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../utils/cn'; 2 | 3 | const IFRAME_ATTRS = { 4 | height: 20, 5 | width: 100, 6 | } as const; 7 | 8 | const GITHUB_STAR_BUTTON_MARGIN_MOBILE = 'mr-10'; 9 | const GITHUB_STAR_BUTTON_MARGIN_DESKTOP = 'md:mr-40'; 10 | 11 | const getSrcUrl = (type: 'star' | 'fork') => 12 | `https://ghbtns.com/github-btn.html?user=mkosir&repo=typescript-style-guide&type=${type}&count=true&size=small` as const; 13 | 14 | export const GithubStats = () => { 15 | return ( 16 |
17 |