├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── prettier.config.js
├── src
├── font
│ └── Caveat
│ │ ├── Caveat-VariableFont_wght.ttf
│ │ ├── OFL.txt
│ │ ├── README.txt
│ │ └── static
│ │ ├── Caveat-Bold.ttf
│ │ ├── Caveat-Medium.ttf
│ │ ├── Caveat-Regular.ttf
│ │ └── Caveat-SemiBold.ttf
├── img
│ └── note.svg
├── index.ts
├── random.ts
├── style
│ ├── main.css
│ └── main.scss
├── template.html
├── types
│ └── globals.d.ts
└── userData.ts
├── tsconfig.json
├── typescript
├── 1.datatypes.ts
├── 2.functions.ts
├── 3.classes-interfaces.ts
├── 3.classes.js
├── 4.narrowing.ts
└── 5.generics.ts
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = false
12 | insert_final_newline = false
13 | max_line_length = 80
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | webpack.config.js
3 |
4 | # src/*.js
5 | # !src/index.js
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | node: false
5 | },
6 | extends: [
7 | 'eslint:recommended',
8 | 'plugin:unicorn/recommended',
9 | 'plugin:@typescript-eslint/recommended',
10 | 'prettier'
11 | ],
12 | globals: {},
13 | overrides: [],
14 | parser: '@typescript-eslint/parser',
15 | parserOptions: {
16 | ecmaVersion: 'latest',
17 | sourceType: 'module',
18 | ecmaFeatures: {
19 | jsx: true
20 | }
21 | },
22 | ignorePatterns: [
23 | '.eslintrc.js',
24 | 'prettier.config.js'
25 | ],
26 | plugins: [
27 | 'unicorn',
28 | '@typescript-eslint',
29 | 'prettier'
30 | ],
31 | rules: {
32 | '@typescript-eslint/no-inferrable-types': 'off',
33 | 'unicorn/no-null': 'off',
34 | 'prettier/prettier': 'error',
35 | 'arrow-body-style': 'off',
36 | 'prefer-arrow-callback': 'off',
37 | 'no-console': [
38 | 'error',
39 | {
40 | allow: [
41 | 'info',
42 | 'error'
43 | ]
44 | }
45 | ],
46 | 'no-var': 'error',
47 | 'no-use-before-define': 'error',
48 | eqeqeq: 'warn',
49 | camelcase: 'error',
50 | 'unicorn/filename-case': 'off'
51 | }
52 | };
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | # OS generated files
107 | .DS_Store
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore artifacts:
2 | dist
3 | coverage
4 |
5 | # Ignore all HTML files:
6 | # *.html
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // Вариант 1
3 | // "prettier.enable": true,
4 | // "eslint.enable": true,
5 | // "eslint.format.enable": true,
6 | // "eslint.run": "onType",
7 | // "[javascript]": {
8 | // "editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
9 | // "editor.formatOnPaste": false, // required
10 | // "editor.formatOnType": false, // required
11 | // "editor.formatOnSave": true, // optional
12 | // "editor.formatOnSaveMode": "file", // required to format on save
13 | // },
14 |
15 | // Вариант 2
16 | "eslint.enable": true,
17 | "eslint.format.enable": true,
18 | "eslint.run": "onType",
19 | "eslint.probe": [
20 | "javascript",
21 | "javascriptreact",
22 | "typescript",
23 | "typescriptreact",
24 | ],
25 | "eslint.rules.customizations": [
26 | {"rule": "prettier/prettier", "severity": "info"}
27 | ],
28 | "[javascript]": {
29 | "editor.defaultFormatter": "dbaeumer.vscode-eslint",
30 | "editor.formatOnPaste": true,
31 | // "editor.formatOnSave": true,
32 | "editor.codeActionsOnSave": [ "source.fixAll.eslint" ],
33 | },
34 | "[typescript]": {
35 | "editor.defaultFormatter": "dbaeumer.vscode-eslint",
36 | "editor.formatOnPaste": true,
37 | // "editor.formatOnSave": true,
38 | "editor.codeActionsOnSave": [ "source.fixAll.eslint" ],
39 | },
40 |
41 | "workbench.colorCustomizations": {
42 | // "editorError.foreground": "#f90000",
43 | "editorInfo.foreground": "#15c804",
44 | "editorWarning.foreground": "#fca80b",
45 | },
46 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Igor Filimonov
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Файлы проекта
2 | Основные файлы проекта находятся в каталоге `./src`.
3 | Примеры использования Typescript находятся в `./typescript`.
4 |
5 | В файл `.vscode/settings.json` добавлены необходимые настройки `VS Code` для интеграции с ESLint и Prettier.
6 | `Prettier` запускается с помощью плагина [eslint-plugin-prettier](https://www.npmjs.com/package/eslint-plugin-prettier).
7 |
8 | ## Плагин для VS Code
9 | Для подсветки ошибок в IDE во время процесса веб-разработки необходимо дополнительно установить расширение [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) для VS Code.
10 |
11 | ## Установка зависимостей проекта
12 | ```sh
13 | npm install
14 | ```
15 |
16 | ## Запуск проекта с помощью сервера
17 | ```sh
18 | npm run serve # Development mode only (webpack-dev-server)
19 | ```
20 |
21 | ## Запуск сборки проекта
22 | ```sh
23 | npm run dev # Development mode
24 | npm run build # Production mode
25 | ```
26 |
27 | ## Проверка форматирования кода проекта
28 | ```sh
29 | npm run prettier
30 | ```
31 |
32 | ## Исправление форматирования кода с помощью Prettier
33 | ```sh
34 | npm run prettier:fix
35 | ```
36 |
37 | ## Проверка качества кода с помощью ESLint
38 | ```sh
39 | npm run lint
40 | ```
41 |
42 | ## Исправление кода с использованием ESLint и Prettier
43 | ```sh
44 | npm run lint:fix
45 | ```
46 |
47 | ## Лицензия
48 | 
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js-middle-typescript",
3 | "version": "1.0.0",
4 | "description": "Web application using TypeScript",
5 | "main": "./src/index.ts",
6 | "scripts": {
7 | "prettier": "prettier --write ./src/**/*.ts",
8 | "prettier:check": "prettier --check ./src/**/*.ts",
9 | "lint": "eslint . --ext .ts",
10 | "lint:fix": "eslint --fix . --ext .ts",
11 | "build": "webpack --mode production --node-env=production",
12 | "dev": "webpack --mode development --node-env=development",
13 | "watch": "webpack --watch --mode development --node-env=development",
14 | "serve": "webpack serve --mode development --node-env=development",
15 | "test": "echo \"Error: no test specified\" && exit 1"
16 | },
17 | "keywords": [],
18 | "author": "Igor Filimonov",
19 | "license": "MIT",
20 | "private": true,
21 | "bugs": {
22 | "url": "https://github.com/iwebexpert/js-middle-typescript/issues"
23 | },
24 | "homepage": "https://github.com/iwebexpert/js-middle-typescript#readme",
25 | "devDependencies": {
26 | "@babel/core": "^7.20.12",
27 | "@babel/eslint-parser": "^7.19.1",
28 | "@babel/preset-env": "^7.20.2",
29 | "@typescript-eslint/eslint-plugin": "^5.53.0",
30 | "@typescript-eslint/parser": "^5.53.0",
31 | "babel-loader": "^9.1.2",
32 | "clean-webpack-plugin": "^4.0.0",
33 | "css-loader": "^6.7.3",
34 | "eslint": "^8.33.0",
35 | "eslint-config-prettier": "^8.6.0",
36 | "eslint-plugin-prettier": "^4.2.1",
37 | "eslint-plugin-unicorn": "^45.0.2",
38 | "eslint-webpack-plugin": "^4.0.0",
39 | "html-webpack-plugin": "^5.5.0",
40 | "mini-css-extract-plugin": "^2.7.2",
41 | "prettier": "2.8.3",
42 | "sass": "^1.57.1",
43 | "sass-loader": "^13.2.0",
44 | "style-loader": "^3.3.1",
45 | "ts-loader": "^9.4.2",
46 | "typescript": "^4.9.5",
47 | "webpack": "^5.75.0",
48 | "webpack-bundle-analyzer": "^4.7.0",
49 | "webpack-cli": "^5.0.1",
50 | "webpack-dev-server": "^4.11.1"
51 | },
52 | "dependencies": {
53 | "axios": "^1.2.3"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5', // es5, all, none
3 | // tabWidth: 2,
4 | // useTabs: false,
5 | // printWidth: 80,
6 | semi: false,
7 | singleQuote: true,
8 | bracketSpacing: true,
9 | arrowParens: 'always', // always, avoid
10 | // endOfLine: 'lf', // lf, crlf, cr, auto
11 |
12 | // requirePragma: true,
13 | // insertPragma: true,
14 |
15 | // HTML, Vue, JSX
16 | singleAttributePerLine: true,
17 | bracketSameLine: false,
18 | }
19 |
--------------------------------------------------------------------------------
/src/font/Caveat/Caveat-VariableFont_wght.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwebexpert/js-middle-typescript/9a85d640a177cafbc370f1a30ee9b56dd8cda021/src/font/Caveat/Caveat-VariableFont_wght.ttf
--------------------------------------------------------------------------------
/src/font/Caveat/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright 2014 The Caveat Project Authors (https://github.com/googlefonts/caveat)
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/src/font/Caveat/README.txt:
--------------------------------------------------------------------------------
1 | Caveat Variable Font
2 | ====================
3 |
4 | This download contains Caveat as both a variable font and static fonts.
5 |
6 | Caveat is a variable font with this axis:
7 | wght
8 |
9 | This means all the styles are contained in a single file:
10 | Caveat-VariableFont_wght.ttf
11 |
12 | If your app fully supports variable fonts, you can now pick intermediate styles
13 | that aren’t available as static fonts. Not all apps support variable fonts, and
14 | in those cases you can use the static font files for Caveat:
15 | static/Caveat-Regular.ttf
16 | static/Caveat-Medium.ttf
17 | static/Caveat-SemiBold.ttf
18 | static/Caveat-Bold.ttf
19 |
20 | Get started
21 | -----------
22 |
23 | 1. Install the font files you want to use
24 |
25 | 2. Use your app's font picker to view the font family and all the
26 | available styles
27 |
28 | Learn more about variable fonts
29 | -------------------------------
30 |
31 | https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
32 | https://variablefonts.typenetwork.com
33 | https://medium.com/variable-fonts
34 |
35 | In desktop apps
36 |
37 | https://theblog.adobe.com/can-variable-fonts-illustrator-cc
38 | https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
39 |
40 | Online
41 |
42 | https://developers.google.com/fonts/docs/getting_started
43 | https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
44 | https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
45 |
46 | Installing fonts
47 |
48 | MacOS: https://support.apple.com/en-us/HT201749
49 | Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
50 | Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
51 |
52 | Android Apps
53 |
54 | https://developers.google.com/fonts/docs/android
55 | https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
56 |
57 | License
58 | -------
59 | Please read the full license text (OFL.txt) to understand the permissions,
60 | restrictions and requirements for usage, redistribution, and modification.
61 |
62 | You can use them in your products & projects – print or digital,
63 | commercial or otherwise.
64 |
65 | This isn't legal advice, please consider consulting a lawyer and see the full
66 | license for all details.
67 |
--------------------------------------------------------------------------------
/src/font/Caveat/static/Caveat-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwebexpert/js-middle-typescript/9a85d640a177cafbc370f1a30ee9b56dd8cda021/src/font/Caveat/static/Caveat-Bold.ttf
--------------------------------------------------------------------------------
/src/font/Caveat/static/Caveat-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwebexpert/js-middle-typescript/9a85d640a177cafbc370f1a30ee9b56dd8cda021/src/font/Caveat/static/Caveat-Medium.ttf
--------------------------------------------------------------------------------
/src/font/Caveat/static/Caveat-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwebexpert/js-middle-typescript/9a85d640a177cafbc370f1a30ee9b56dd8cda021/src/font/Caveat/static/Caveat-Regular.ttf
--------------------------------------------------------------------------------
/src/font/Caveat/static/Caveat-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwebexpert/js-middle-typescript/9a85d640a177cafbc370f1a30ee9b56dd8cda021/src/font/Caveat/static/Caveat-SemiBold.ttf
--------------------------------------------------------------------------------
/src/img/note.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import updateUser from './userData'
2 | import './style/main.scss'
3 |
4 | import noteImg from './img/note.svg'
5 |
6 | // const mainImg = document.querySelector('.main-img')
7 | const mainImg = document.querySelector('.main-img') as HTMLImageElement | null
8 | if (mainImg) {
9 | mainImg.src = noteImg
10 | }
11 |
12 | const mainButton = document.querySelector('.main-btn')
13 | if (mainButton) {
14 | mainButton.addEventListener('click', updateUser)
15 | }
16 |
17 | updateUser()
18 |
--------------------------------------------------------------------------------
/src/random.ts:
--------------------------------------------------------------------------------
1 | export function getRandomInt(min: number, max: number): number {
2 | min = Math.ceil(min)
3 | max = Math.floor(max)
4 |
5 | // debugger
6 | return Math.floor(Math.random() * (max - min + 1)) + min
7 | }
8 |
--------------------------------------------------------------------------------
/src/style/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: brown;
3 | }
--------------------------------------------------------------------------------
/src/style/main.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Caveat';
3 | src: url('../font/Caveat/Caveat-VariableFont_wght.ttf') format('truetype');
4 | font-weight: 400;
5 | font-style: normal;
6 | }
7 |
8 | $bg-color: #66ba7c;
9 | $page-color: #eeecec;
10 | $box-shadow: 0 12px 20px rgba(0, 0, 0, 0.15), 0 8px 8px rgba(0, 0, 0, 0.15);
11 |
12 |
13 | body {
14 | background-color: $bg-color;
15 | font-family: 'Caveat', sans-serif;
16 | display: flex;
17 | align-items: center;
18 | justify-content: center;
19 | height: 100vh;
20 | // overflow: hidden;
21 | padding: 30px;
22 | margin: 0;
23 | box-sizing: border-box;
24 | }
25 |
26 | .container {
27 | width: 768px;
28 | background-color: $page-color;
29 | box-shadow: $box-shadow;
30 | border-radius: 10px;
31 | padding: 40px 15px;
32 | text-align: center;
33 | }
34 |
35 | img.main-img {
36 | width: 108px;
37 | margin-bottom: 25px;
38 | }
39 |
40 | .header {
41 | font-size: 24px;
42 | color: #999;
43 | letter-spacing: 2px;
44 | }
45 |
46 | .user-info {
47 | font-size: 28px;
48 | letter-spacing: 1.5px;
49 | margin: 25px auto;
50 | }
51 |
52 | .main-btn {
53 | margin-top: 30px;
54 | background-color: $bg-color;
55 | color: $page-color;
56 | border: 0;
57 | border-radius: 12px;
58 | padding: 12px 35px;
59 | font-size: 18px;
60 | cursor: pointer;
61 |
62 | &:hover {
63 | transform: scale(1.05);
64 | }
65 | }
--------------------------------------------------------------------------------
/src/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 | <%= htmlWebpackPlugin.options.title %>
14 |
15 |
16 |
17 |
![]()
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/types/globals.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.svg' {
2 | const content: string
3 | export default content
4 | }
5 |
--------------------------------------------------------------------------------
/src/userData.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { getRandomInt } from './random'
3 |
4 | interface IView {
5 | name: string
6 | amount: number
7 | email: string
8 | }
9 |
10 | function updateView(data: IView): void {
11 | const elementName = document.querySelector('.user-name')
12 | if (elementName) {
13 | elementName.textContent = data.name
14 | }
15 |
16 | const elementAmount = document.querySelector('.user-amount')
17 | if (elementAmount) {
18 | elementAmount.innerHTML = data.amount + ' ₽'
19 | }
20 |
21 | const elementEmail = document.querySelector('.user-email')
22 | if (elementEmail) {
23 | elementEmail.textContent = data.email
24 | }
25 | }
26 |
27 | function updateUser() {
28 | // fetch(`https://jsonplaceholder.typicode.com/users/${getRandomInt(1, 10)}`)
29 | // .then((result) => result.json())
30 | // .then((result) => {
31 | // updateView({
32 | // name: result.name,
33 | // amount: getRandomInt(1000, 10_000),
34 | // email: result.email,
35 | // })
36 | // })
37 |
38 | axios
39 | .get(`https://jsonplaceholder.typicode.com/users/${getRandomInt(1, 10)}`)
40 | .then((result) => {
41 | updateView({
42 | name: result.data.name,
43 | amount: getRandomInt(1000, 10_000),
44 | email: result.data.email,
45 | })
46 | })
47 | }
48 |
49 | export default updateUser
50 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "ES2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16 | // "jsx": "preserve", /* Specify what JSX code is generated. */
17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26 |
27 | /* Modules */
28 | "module": "commonjs", /* Specify what module code is generated. */
29 | // "rootDir": "./src", /* Specify the root folder within your source files. */
30 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
38 | // "resolveJsonModule": true, /* Enable importing .json files. */
39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
40 |
41 | /* JavaScript Support */
42 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
43 | // "checkJs": false, /* Enable error reporting in type-checked JavaScript files. */
44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
45 |
46 | /* Emit */
47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
50 | "sourceMap": true, /* Create source map files for emitted JavaScript files. */
51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
52 | // "outDir": "./dist", /* Specify an output folder for all emitted files. */
53 | // "removeComments": true, /* Disable emitting comments. */
54 | // "noEmit": true, /* Disable emitting files from a compilation. */
55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
63 | // "newLine": "crlf", /* Set the newline character for emitting files. */
64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
70 |
71 | /* Interop Constraints */
72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
77 |
78 | /* Type Checking */
79 | "strict": true, /* Enable all strict type-checking options. */
80 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
98 |
99 | /* Completeness */
100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/typescript/1.datatypes.ts:
--------------------------------------------------------------------------------
1 | // Базовые типы данных
2 | const userName: string = 'Olga'
3 | const userAge: number = 28
4 | const userAccount: bigint = BigInt(255)
5 | // const userAccount: bigint = 255n // ES2020+
6 |
7 | const isNull: null = null
8 | const isUndefined: undefined = undefined
9 |
10 | const isAdmin: boolean = true
11 |
12 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
13 | const userTest: any = []
14 |
15 | console.info(
16 | userName,
17 | userAge,
18 | userAccount,
19 | isNull,
20 | isUndefined,
21 | isAdmin,
22 | userTest
23 | )
24 |
25 | // Массивы и кортежи
26 | const arrayNums: number[] = [1, 2, 3]
27 | const arrayBoolean: boolean[] = [true, false, true]
28 | const arrayBoolean2: Array = [true, false, true]
29 | const arrayNumsAndStrings: (number | string)[] = [1, '2', 3, '4']
30 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
31 | const arrayAny: any[] = [1, true, 'Oleg']
32 |
33 | const arrayUser1: [number, boolean, string] = [1, true, 'Oleg']
34 | arrayUser1[0] = 10
35 | arrayUser1[1] = false
36 | arrayUser1[2] = 'Олег'
37 |
38 | console.info(
39 | arrayNums,
40 | arrayBoolean,
41 | arrayBoolean2,
42 | arrayNumsAndStrings,
43 | arrayAny,
44 | arrayUser1
45 | )
46 |
47 | // Объединения и перечисления (Enums)
48 | // Пример 1
49 | function myId(id: number | string) {
50 | if (typeof id === 'string') {
51 | console.info(id.toLocaleLowerCase())
52 | } else {
53 | console.info(id)
54 | }
55 | }
56 |
57 | myId('5')
58 | myId(5)
59 | myId('Test')
60 |
61 | // Пример 2
62 | function printColors(color: string | string[]) {
63 | if (Array.isArray(color)) {
64 | console.info('Цвета: ' + color.join(' и '))
65 | } else {
66 | console.info('Цвет: ' + color)
67 | }
68 | }
69 |
70 | printColors('красный')
71 | printColors(['красный', 'белый'])
72 |
73 | // Пример 3
74 | function getItemFirst(items: number[] | string) {
75 | return items.slice(0, 1)
76 | }
77 |
78 | console.info(getItemFirst([1, 2, 3, 4]))
79 | console.info(getItemFirst('Test'))
80 |
81 | // Перечисления
82 | const ANSWER_YES = 'YES'
83 |
84 | // Пример 1
85 | enum Answer {
86 | Yes = 'YES',
87 | No = 'NO',
88 | // No = '1234'.length,
89 | }
90 |
91 | console.info(ANSWER_YES, Answer.Yes, Answer.No)
92 |
93 | // Пример 2
94 | enum FileAccess {
95 | // Read = 2,
96 | // Write = 4,
97 | Read = 1 << 1,
98 | Write = 1 << 2,
99 | ReadWrite = Read | Write,
100 | }
101 |
102 | console.info(FileAccess.Read, FileAccess.Write, FileAccess.ReadWrite)
103 |
104 | // Псевдонимы типов данных (пользовательские типы данных)
105 | type BasePoint = {
106 | x: number
107 | y: number
108 | }
109 | const point1: BasePoint = {
110 | x: 10,
111 | y: 10,
112 | }
113 |
114 | function printPointCoords(point: BasePoint) {
115 | console.info(`x=${point.x}; y=${point.y}`)
116 | }
117 |
118 | console.info(point1)
119 | printPointCoords(point1)
120 |
121 | type FullPoint = BasePoint & {
122 | readonly title: string
123 | description?: string
124 | }
125 |
126 | const point2: FullPoint = {
127 | x: 100,
128 | y: 100,
129 | title: 'point2',
130 | description: 'Описание point2',
131 | }
132 |
133 | // point2.title = 'Test'
134 |
135 | console.info(point2)
136 |
--------------------------------------------------------------------------------
/typescript/2.functions.ts:
--------------------------------------------------------------------------------
1 | // Базовая работа с функциями
2 | console.info('Базовая работа с функциями')
3 | function plus(x: number = 1, y?: number): number {
4 | return x + (y || 1)
5 | }
6 |
7 | console.info(plus(10, 20))
8 | console.info(plus())
9 |
10 | function printNumber(x: number): void {
11 | console.info(`x=${x}`)
12 | }
13 |
14 | printNumber(10)
15 |
16 | // Остаточные параметры (rest parameters)
17 | function multiply(m: number, ...parameters: number[]) {
18 | // console.info(parameters)
19 | return parameters.map(function (item) {
20 | return m * item
21 | })
22 | }
23 |
24 | console.info(multiply(100, 1, 10, 20, 50))
25 |
26 | // Деструктуризация параметров (parameter destructuring)
27 | type PlusParameters = {
28 | x: number
29 | y: number
30 | }
31 | function plus2({ x = 1, y }: { x: number; y: number }): number {
32 | return x + (y || 1)
33 | }
34 |
35 | console.info(plus2({ x: 10, y: 20 }))
36 |
37 | function plus3({ x = 1, y }: PlusParameters): number {
38 | return x + (y || 1)
39 | }
40 |
41 | console.info(plus3({ x: 10, y: 20 }))
42 |
43 | function plus4(items: PlusParameters): number {
44 | return items.x + (items.y || 1)
45 | }
46 |
47 | console.info(plus4({ x: 10, y: 20 }))
48 |
49 | // Тип возвращаемого значения Never
50 | function getMessage(message: string): never {
51 | // while (true) {}
52 | throw new Error(message)
53 | }
54 |
55 | try {
56 | getMessage('Error 1234')
57 | } catch (error) {
58 | if (error instanceof Error) {
59 | console.info(error.message)
60 | }
61 |
62 | console.info((error as Error).message)
63 | console.info((error).message)
64 | } finally {
65 | console.info('finally')
66 | }
67 |
68 | // Функции обратного вызова. Стрелочные функции
69 | function printMessage(callback: (message: string) => void) {
70 | callback('Привет!')
71 | }
72 |
73 | function callback1(text: string) {
74 | console.info(text)
75 | }
76 |
77 | printMessage(callback1)
78 |
79 | // Стрелочные функции
80 | const callback2 = (text: string): void => console.info(text)
81 | printMessage(callback2)
82 |
83 | printMessage((text) => console.info(text))
84 |
85 | type CallbackParameters = {
86 | (text: string): void
87 | }
88 |
89 | const callback3: CallbackParameters = (text) => console.info(text)
90 | printMessage(callback3)
91 |
92 | // Перегрузка функций
93 | // Сигнатуры перегрузки
94 | function createDate(timestamp: number): Date
95 | function createDate(timestamp: number, month: number, year: number): Date
96 | // Сигнатура реализации
97 | function createDate(
98 | dayOrTimestamp: number,
99 | month?: number,
100 | year?: number
101 | ): Date {
102 | return month !== undefined && year !== undefined
103 | ? new Date(year, month, dayOrTimestamp)
104 | : new Date(dayOrTimestamp * 1000)
105 | }
106 |
107 | console.info(createDate(1_677_935_727))
108 | console.info(createDate(5, 2, 2023))
109 | // console.info(createDate(5, 2))
110 |
111 | // Перегрузка стрелочных функций
112 | type CreateDate = {
113 | (timestamp: number): Date
114 | (timestamp: number, month: number, year: number): Date
115 | }
116 | const createDate2: CreateDate = (
117 | dayOrTimestamp: number,
118 | month?: number,
119 | year?: number
120 | ): Date => {
121 | return month !== undefined && year !== undefined
122 | ? new Date(year, month, dayOrTimestamp)
123 | : new Date(dayOrTimestamp * 1000)
124 | }
125 |
126 | console.info(createDate2(1_677_935_727))
127 | console.info(createDate2(5, 2, 2023))
128 | // console.info(createDate2(5, 2))
129 |
--------------------------------------------------------------------------------
/typescript/3.classes-interfaces.ts:
--------------------------------------------------------------------------------
1 | // Классы в TypeScript
2 | console.info('Классы в TypeScript')
3 | abstract class Figure {
4 | info() {
5 | console.info('Это метод info() класса Figure')
6 | }
7 | abstract printInfo(isShow: boolean): string
8 | }
9 |
10 | // abstract class Figure2 extends Figure {
11 | // abstract printInfo2(isShow: boolean): string
12 | // }
13 |
14 | // const figure1 = new Figure()
15 | // figure1.info()
16 |
17 | class Circle extends Figure {
18 | private radius: number = 25
19 | public static readonly PI: number = 3.14
20 | public setRadius(radius: number): void {
21 | if (radius > 0) {
22 | this.radius = radius
23 | }
24 | }
25 |
26 | public getRadius() {
27 | return this.radius
28 | }
29 |
30 | info() {
31 | console.info('Это метод info() класса Circle')
32 | super.info()
33 | }
34 |
35 | printInfo(isShow: boolean = false): string {
36 | const message = `Радиус круга равен ${this.radius}`
37 | if (isShow) {
38 | console.info(message)
39 | }
40 |
41 | return message
42 | }
43 | }
44 |
45 | interface IRect {
46 | width: number
47 | info(): void
48 | printInfo(isShow: boolean): string
49 | }
50 |
51 | // interface IRect {
52 | // title: string
53 | // }
54 |
55 | interface IRectTitle extends IRect {
56 | title: string
57 | }
58 |
59 | interface IRect2 {
60 | readonly description: string
61 | }
62 |
63 | // type ExamleType = {
64 | // title: string
65 | // }
66 |
67 | // type ExamleType2 = ExamleType & {
68 | // description: string
69 | // }
70 |
71 | // type ExamleType3 = string
72 |
73 | class Rect extends Figure implements IRectTitle, IRect2 {
74 | public width: number
75 | protected height: number
76 | title: string = 'Название прямоугольника'
77 | description: string = 'Описание прямоугольника'
78 | constructor(width: number, height: number) {
79 | super()
80 | this.width = width
81 | this.height = height
82 | }
83 |
84 | info() {
85 | console.info('Это метод info() класса Rect')
86 | super.info()
87 | }
88 |
89 | printInfo(isShow: boolean = false): string {
90 | const message = `Ширина фигуры равна ${this.width}, высота ${this.height}`
91 | if (isShow) {
92 | console.info(message)
93 | }
94 |
95 | return message
96 | }
97 | }
98 |
99 | class Square extends Rect {
100 | constructor(side: number) {
101 | super(side, side)
102 |
103 | // this.height = 2000
104 | // this.width = 20
105 | }
106 |
107 | info() {
108 | console.info('Это метод info() класса Square')
109 | super.info()
110 | }
111 | }
112 |
113 | const circle1 = new Circle()
114 | circle1.info()
115 | console.info('Radius=' + circle1.getRadius())
116 | circle1.setRadius(200)
117 | console.info('Radius=' + circle1.getRadius())
118 | circle1.printInfo(true)
119 | console.info(Circle.PI)
120 | // Circle.PI = 3.1415
121 | console.info(Circle.PI)
122 |
123 | const rect1 = new Rect(200, 100)
124 | rect1.info()
125 | rect1.printInfo(true)
126 |
127 | const square1 = new Square(500)
128 | square1.info()
129 | console.info(square1.printInfo())
130 |
131 | // Интерфейсы для объектов, массивов и функций
132 | interface IRectItem {
133 | readonly width: number
134 | readonly height: number
135 | printInfo(isShow?: boolean): string
136 | }
137 |
138 | const rect2: IRectItem = {
139 | width: 100,
140 | height: 120,
141 | printInfo(isShow: boolean = false) {
142 | const message = `Ширина фигуры равна ${this.width}, высота ${this.height}`
143 | if (isShow) {
144 | console.info(message)
145 | }
146 |
147 | return message
148 | },
149 | }
150 |
151 | console.info(rect2, rect2.printInfo())
152 |
153 | interface DictionaryColor {
154 | [index: string]: string
155 | }
156 |
157 | const color1: DictionaryColor = {}
158 | color1['red'] = 'Красный'
159 | color1['blue'] = 'Синий'
160 | console.info(color1, color1['red'])
161 |
162 | interface DictionaryColor2 {
163 | [index: number]: string
164 | }
165 |
166 | const color2: DictionaryColor2[] = []
167 | color2[0] = 'Красный'
168 | color2[1] = 'Синий'
169 | color2.push('Белый')
170 | console.info(color2, color2[1])
171 |
172 | const color3: string[] = []
173 | color3.push('Белый')
174 | console.info(color3)
175 |
176 | interface IUserBuilder {
177 | (name: string, age: number): string
178 | (name: string, age: number, department: string): string
179 | }
180 |
181 | const createUser: IUserBuilder = (name, age, department = '') =>
182 | `Пользователь ${name}; возраст ${age}; ${department}`
183 |
184 | console.info(createUser('Иван', 30))
185 | console.info(createUser('Иван', 30, 'Front-end'))
--------------------------------------------------------------------------------
/typescript/3.classes.js:
--------------------------------------------------------------------------------
1 | class User {
2 | #name = 'Olga'
3 | age = 28
4 | static PI = 3.14
5 | getName() {
6 | return this.#name
7 | }
8 |
9 | static info() {
10 | return new Date()
11 | }
12 | }
13 |
14 | // const user1 = new User()
15 | // user1.getName()
16 | console.info(User.PI)
17 | console.info(User.info())
18 |
--------------------------------------------------------------------------------
/typescript/4.narrowing.ts:
--------------------------------------------------------------------------------
1 | // Что такое Narrowing
2 | console.info('Что такое Narrowing')
3 | // Пример 1
4 | function getId(id: number | string) {
5 | if (typeof id === 'string') {
6 | console.info(id.toLocaleLowerCase())
7 | } else {
8 | console.info(id)
9 | }
10 | }
11 |
12 | getId('5')
13 | getId(5)
14 | getId('Test')
15 |
16 | // Пример 2
17 | function showAllColors(colors: string | string[] | null) {
18 | if (colors && typeof colors === 'object') {
19 | for (const s of colors) {
20 | console.info(s)
21 | }
22 | } else if (typeof colors === 'string') {
23 | console.info(colors)
24 | }
25 | }
26 |
27 | console.info(typeof null, typeof {})
28 | showAllColors(['red', 'blue'])
29 | showAllColors('red')
30 | showAllColors(null)
31 |
32 | // Пример 3
33 | function showAllColors2(colors: string | string[] | null) {
34 | if (colors !== null) {
35 | if (typeof colors === 'object') {
36 | for (const s of colors) {
37 | console.info(s)
38 | }
39 | } else if (typeof colors === 'string') {
40 | console.info(colors)
41 | }
42 | }
43 | }
44 |
45 | showAllColors2(['red', 'blue'])
46 | showAllColors2('red')
47 | showAllColors2(null)
48 |
49 | // Пример 4
50 | interface MultiplyParameters {
51 | value: number | null | undefined
52 | }
53 | function multiply2(object: MultiplyParameters, n: number) {
54 | // eslint-disable-next-line eqeqeq
55 | if (object.value != null) {
56 | console.info(object.value)
57 | object.value *= n
58 | console.info(object.value)
59 | }
60 | }
61 |
62 | multiply2({ value: 10 }, 2)
63 | // eslint-disable-next-line eqeqeq
64 | console.info(null == undefined, null === undefined)
65 |
66 | // Сужение типов с помощью оператора in и instanceof
67 | type Fish = { swim: () => void; title: string }
68 | type Bird = { fly: () => void; title: string }
69 |
70 | function go(animal: Fish | Bird) {
71 | if ('swim' in animal) {
72 | return animal.swim()
73 | }
74 | return animal.fly()
75 | }
76 |
77 | const fish1: Fish | Bird = {
78 | title: 'fish1',
79 | swim() {
80 | console.info('Рыба плывет')
81 | },
82 | }
83 |
84 | console.info(go(fish1))
85 |
86 | // instanceof
87 | function writeToLog(value: Date | string) {
88 | if (value instanceof Date) {
89 | console.info(value.toUTCString())
90 | } else {
91 | console.info(value.toUpperCase())
92 | }
93 | }
94 |
95 | writeToLog(new Date())
96 | writeToLog('Test')
97 |
98 | // Присвоения. Анализ потока управления
99 | let userId = Math.random() < 0.5 ? 1 : 'One'
100 |
101 | userId = 2
102 | console.info(userId)
103 |
104 | userId = 'two'
105 | console.info(userId)
106 |
107 | // userId = true // Error
108 |
109 | function test() {
110 | let id: string | number | boolean
111 |
112 | id = Math.random() < 0.5
113 | console.info(id)
114 |
115 | if (id) {
116 | id = 'One'
117 | console.info(id)
118 | } else {
119 | id = 1
120 | console.info(id)
121 | }
122 |
123 | return id
124 | }
125 |
126 | let userId2 = test()
127 | userId2 = 2
128 | console.info(userId2)
129 |
130 | userId2 = 'two'
131 | console.info(userId2)
132 |
133 | // userId2 = true // Error
134 |
135 | // Пользовательские защитники типов
136 | function isFish(pet: Fish | Bird): pet is Fish {
137 | return (pet as Fish).swim !== undefined
138 | }
139 |
140 | function getFishOrBird(): Fish | Bird {
141 | const fish: Fish = {
142 | title: `fish ${Math.random()}`,
143 | swim() {
144 | console.info('Рыба плывет')
145 | },
146 | }
147 |
148 | const bird: Bird = {
149 | title: `bird ${Math.random()}`,
150 | fly() {
151 | console.info('Птица летит')
152 | },
153 | }
154 |
155 | return Math.random() < 0.5 ? fish : bird
156 | }
157 |
158 | const pet = getFishOrBird()
159 |
160 | if (isFish(pet)) {
161 | pet.swim()
162 | } else {
163 | pet.fly()
164 | }
165 |
166 | const pets: (Fish | Bird)[] = [
167 | getFishOrBird(),
168 | getFishOrBird(),
169 | getFishOrBird(),
170 | getFishOrBird(),
171 | getFishOrBird(),
172 | getFishOrBird(),
173 | getFishOrBird(),
174 | ]
175 |
176 | // eslint-disable-next-line unicorn/no-array-callback-reference
177 | const fishOnly: Fish[] = pets.filter(isFish)
178 | console.info(fishOnly)
179 |
180 | const fishOnly2: Fish[] = pets.filter((element) => isFish(element)) as Fish[]
181 | console.info(fishOnly2)
182 |
183 | // Более сложный случай
184 | const fishTitleItem1 = fishOnly2[0]?.title || 'fish1'
185 | console.info(fishTitleItem1)
186 | const fishOnly3: Fish[] = pets.filter((pet): pet is Fish => {
187 | // Пользовательские условия поиска
188 | if (pet.title === fishTitleItem1) return false
189 | return isFish(pet)
190 | })
191 | console.info(fishOnly3)
192 |
193 | // Исключающие объединения
194 | // interface Figure {
195 | // type: 'circle' | 'square'
196 | // radius?: number
197 | // side?: number
198 | // }
199 |
200 | // function getArea(figure: Figure) {
201 | // if (figure.type === 'circle') {
202 | // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
203 | // return Math.PI * figure.radius! ** 2
204 | // }
205 | // }
206 |
207 | interface FigureCircle {
208 | type: 'circle'
209 | radius: number
210 | }
211 |
212 | interface FigureSquare {
213 | type: 'square'
214 | side: number
215 | }
216 |
217 | interface FigureRect {
218 | type: 'rect'
219 | width: number
220 | height: number
221 | }
222 |
223 | type MyFigure = FigureCircle | FigureSquare | FigureRect
224 |
225 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
226 | function getArea(figure: MyFigure) {
227 | if (figure.type === 'circle') {
228 | return Math.PI * figure.radius ** 2
229 | }
230 | }
231 |
232 | function getArea2(figure: MyFigure) {
233 | switch (figure.type) {
234 | case 'circle': {
235 | return Math.PI * figure.radius ** 2
236 | }
237 | case 'square': {
238 | return figure.side ** 2
239 | }
240 | case 'rect': {
241 | return figure.width * figure.height
242 | }
243 | default: {
244 | const _check: never = figure
245 | return _check
246 | }
247 | }
248 | }
249 |
250 | const figure1: FigureCircle = {
251 | type: 'circle',
252 | radius: 10,
253 | }
254 | console.info(getArea2(figure1))
255 |
--------------------------------------------------------------------------------
/typescript/5.generics.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | // Введение в Дженерики
3 | console.info('Введение в Дженерики')
4 |
5 | function searchString(array: string[], query: string): boolean {
6 | for (const value of array) {
7 | if (value === query) {
8 | return true
9 | }
10 | }
11 | return false
12 | }
13 |
14 | function searchNumber(array: number[], query: number): boolean {
15 | for (const value of array) {
16 | if (value === query) {
17 | return true
18 | }
19 | }
20 | return false
21 | }
22 |
23 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
24 | function searchAny(array: any[], query: any): boolean {
25 | for (const value of array) {
26 | if (value === query) {
27 | return true
28 | }
29 | }
30 | return false
31 | }
32 |
33 | searchAny(['Test', 1234, undefined, {}], 'Test')
34 |
35 | function search(array: T[], query: T): boolean {
36 | for (const value of array) {
37 | if (value === query) {
38 | return true
39 | }
40 | }
41 | return false
42 | }
43 |
44 | console.info(search(['Test', 'Red'], 'Test'))
45 | console.info(search([1, 2], 3))
46 |
47 | console.info(search([1, 2], 3))
48 |
49 | // Необходимо указание типов данных
50 | function swapExample(a: T, b: T): void {
51 | const temporary = a
52 | a = b
53 | b = temporary
54 | }
55 |
56 | const userInfoA: number = 1
57 | const userInfoB: string = 'One'
58 | swapExample(userInfoA, userInfoB)
59 |
60 | // Дженерики. Типы и интерфейсы
61 | type CartInfo = {
62 | id: number
63 | products: string[]
64 | currency: T
65 | }
66 |
67 | const cart1: CartInfo = {
68 | id: 1,
69 | products: [],
70 | currency: 1,
71 | }
72 | console.info(cart1)
73 |
74 | const cart2: CartInfo = {
75 | id: 1,
76 | products: [],
77 | currency: 'Ruble',
78 | }
79 | console.info(cart2)
80 |
81 | interface ICartInfo {
82 | id: number
83 | products: string[]
84 | currency: T
85 | }
86 |
87 | const cart3: ICartInfo = {
88 | id: 1,
89 | products: [],
90 | currency: 1,
91 | }
92 | console.info(cart3)
93 |
94 | const cart4: ICartInfo = {
95 | id: 1,
96 | products: [],
97 | currency: 'Ruble',
98 | }
99 | console.info(cart4)
100 |
101 | // Оператор keyof
102 | interface IUser {
103 | name: string
104 | age: number
105 | }
106 |
107 | type UserKeys = keyof IUser
108 |
109 | const userName2: UserKeys = 'name'
110 | console.info(userName2)
111 |
112 | const user = {
113 | name: 'Иван',
114 | age: 30,
115 | department: 'Front-end',
116 | }
117 |
118 | type UserKeys2 = keyof typeof user
119 |
120 | // eslint-disable-next-line unicorn/no-array-for-each
121 | Object.keys(user).forEach((key) => {
122 | console.info(user[key as UserKeys2])
123 | console.info(user[key])
124 | })
125 |
126 | interface ObjectConstructor {
127 | keys(o: T): (keyof T)[]
128 | }
129 |
130 | for (const key of Object.keys(user)) {
131 | console.info(user[key])
132 | }
133 |
134 | // Дженерики и Классы
135 | // Пример 1
136 | interface ICheckId {
137 | getId(): T
138 | }
139 |
140 | class CheckId implements ICheckId {
141 | constructor(private readonly id: T) {
142 | this.id = id
143 | }
144 |
145 | getId(): T {
146 | return this.id
147 | }
148 | }
149 |
150 | const checkId = new CheckId(1)
151 | console.info(checkId)
152 |
153 | // Пример 2
154 | class Queue {
155 | private data: T[] = []
156 |
157 | public push = (item: T) => this.data.push(item)
158 | public pop = (): T | undefined => this.data.shift()
159 | }
160 |
161 | const queue1 = new Queue()
162 | queue1.push(1)
163 | console.info(queue1.pop())
164 | queue1.push(2)
165 | console.info(queue1.pop())
166 |
167 | // Ограничения Дженериков
168 | // Пример 1
169 | interface ILength {
170 | length: number
171 | }
172 |
173 | function getLength(value: T): number {
174 | return value.length
175 | }
176 |
177 | console.info(getLength(['Красный', 'Синий']))
178 | console.info(getLength('Красный'))
179 | // console.info(getLength(1))
180 |
181 | // Пример 2
182 | function getValueOfObject(object: T, key: K): T[K] {
183 | return object[key]
184 | }
185 |
186 | console.info(getValueOfObject(user, 'name'))
187 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin")
4 | // const { CleanWebpackPlugin } = require('clean-webpack-plugin')
5 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
6 | const ESLintPlugin = require('eslint-webpack-plugin')
7 | const isProduction = process.env.NODE_ENV === 'production'
8 | // console.log('isProduction', isProduction)
9 |
10 | const styleLoaderHandler = isProduction ? MiniCssExtractPlugin.loader : 'style-loader'
11 |
12 | module.exports = {
13 | // mode: 'production',
14 | entry: path.resolve(__dirname, 'src', 'index.ts'),
15 | // entry: path.resolve(__dirname, 'typescript', '5.generics.ts'),
16 | output: {
17 | path: path.resolve(__dirname, 'dist'),
18 | filename: '[name][contenthash].js',
19 | clean: true,
20 | assetModuleFilename: 'assets/[name][hash][ext]'
21 | },
22 | resolve: {
23 | // Add `.ts` and `.tsx` as a resolvable extension.
24 | extensions: [".ts", ".tsx", ".js"],
25 | // Add support for TypeScripts fully qualified ESM imports.
26 | extensionAlias: {
27 | ".js": [".js", ".ts"],
28 | ".cjs": [".cjs", ".cts"],
29 | ".mjs": [".mjs", ".mts"]
30 | }
31 | },
32 | devtool: (isProduction) ? 'source-map' : 'inline-source-map',
33 | devServer: {
34 | static: {
35 | directory: path.resolve(__dirname, 'dist'),
36 | },
37 | host: 'localhost',
38 | port: 3000,
39 | open: true,
40 | hot: true,
41 | compress: true,
42 | historyApiFallback: true,
43 | onListening: function(devServer){
44 | if(isProduction){
45 | throw new Error('webpack-dev-server is not allowed')
46 | }
47 |
48 | const port = devServer.server.address().port
49 | console.log(`Port: ${port}`)
50 | },
51 | },
52 | module: {
53 | rules: [
54 | {
55 | test: /\.s?css$/i,
56 | use: [styleLoaderHandler, "css-loader", 'sass-loader'],
57 | },
58 | {
59 | test: /\.(png|svg|jpg|jpeg|gif)$/i,
60 | type: 'asset/resource',
61 | },
62 | {
63 | test: /\.(woff|woff2|eot|ttf|otf)$/i,
64 | type: 'asset/resource',
65 | generator: {
66 | filename: 'fonts/[hash][ext]',
67 | },
68 | },
69 | // all files with a `.ts`, `.cts`, `.mts` or `.tsx` extension will be handled by `ts-loader`
70 | { test: /\.([cm]?ts|tsx)$/, loader: "ts-loader" },
71 | {
72 | test: /\.m?js$/,
73 | exclude: /node_modules/,
74 | use: {
75 | loader: "babel-loader",
76 | options: {
77 | cacheDirectory: true,
78 | presets: ['@babel/preset-env']
79 | }
80 | }
81 | },
82 | ],
83 | },
84 | plugins: [
85 | new HtmlWebpackPlugin({
86 | template: path.resolve(__dirname, 'src', 'template.html'),
87 | filename: 'index.html',
88 | favicon: path.resolve(__dirname, 'src', 'img', 'note.svg'),
89 | title: 'Приложение для электронных чеков v1.0.0',
90 | }),
91 | // new CleanWebpackPlugin(),
92 | new MiniCssExtractPlugin({
93 | filename: 'css/[name][contenthash].css'
94 | }),
95 | // new BundleAnalyzerPlugin({
96 | // analyzerMode: 'static',
97 | // defaultSizes: 'gzip',
98 | // analyzerPort: 3001,
99 | // reportTitle: 'Основной отчет',
100 | // reportFilename: 'stats.html',
101 | // openAnalyzer: false,
102 | // generateStatsFile: true,
103 | // statsFilename: 'stats.json',
104 | // }),
105 | new ESLintPlugin({
106 | extensions: ['js', 'ts'],
107 | // fix: true,
108 | }),
109 | ],
110 | }
--------------------------------------------------------------------------------