├── .dockerignore ├── modules ├── 10-basics │ ├── 50-arrays │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ ├── EXERCISE.md │ │ │ └── README.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ ├── EXERCISE.md │ │ │ └── README.md │ │ ├── index.ts │ │ └── test.ts │ ├── 55-objects │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 60-enums │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 80-any │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── test.ts │ │ └── index.ts │ ├── 90-modules │ │ ├── Makefile │ │ ├── test.ts │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ └── index.ts │ ├── 10-hello-world │ │ ├── Makefile │ │ ├── index.ts │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ ├── data.yml │ │ │ └── README.md │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ ├── data.yml │ │ │ └── README.md │ │ └── test.ts │ ├── 30-variables │ │ ├── Makefile │ │ ├── index.ts │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ └── test.ts │ ├── 70-type-aliases │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 40-named-functions │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 45-anonymous-functions │ │ ├── Makefile │ │ ├── ru │ │ │ ├── data.yml │ │ │ ├── EXERCISE.md │ │ │ └── README.md │ │ ├── en │ │ │ ├── data.yml │ │ │ ├── EXERCISE.md │ │ │ └── README.md │ │ ├── index.ts │ │ └── test.ts │ ├── 20-typescript-as-a-second-language │ │ ├── Makefile │ │ ├── index.ts │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ └── test.ts │ ├── description.ru.yml │ └── description.en.yml ├── 20-functions │ ├── 30-void │ │ ├── Makefile │ │ ├── index.ts │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ └── test.ts │ ├── 35-never │ │ ├── Makefile │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── index.ts │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ └── test.ts │ ├── 40-unknown │ │ ├── Makefile │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 70-destructuring │ │ ├── Makefile │ │ ├── index.ts │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ └── test.ts │ ├── 80-rest-spread │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 90-type-narrowing │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ ├── test.ts │ │ └── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ ├── 85-function-overloads │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── test.ts │ │ └── index.ts │ ├── 10-function-as-parameter │ │ ├── Makefile │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 20-optional-parameters-in-callbacks │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── description.en.yml │ └── description.ru.yml ├── 22-arrays │ ├── 50-tuples │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 20-type-annotations │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 40-readonly-arrays │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ └── test.ts │ ├── 25-multi-dimensional-arrays │ │ ├── Makefile │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── description.en.yml │ └── description.ru.yml ├── 25-types │ ├── 22-nullable │ │ ├── Makefile │ │ ├── index.ts │ │ ├── test.ts │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ └── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ ├── 10-type-as-sets │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ ├── assets │ │ │ └── some_type.png │ │ └── test.ts │ ├── 20-union-types │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── assets │ │ │ └── number_or_string.png │ │ ├── index.ts │ │ └── test.ts │ ├── 25-literal-types │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 40-assignability │ │ ├── Makefile │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── test.ts │ │ └── index.ts │ ├── 50-type-hierarcy │ │ ├── Makefile │ │ ├── assets │ │ │ └── hierarcy_circle.png │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 70-variability │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 30-intersection-types │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── assets │ │ │ └── one_hundred_order.png │ │ ├── test.ts │ │ └── index.ts │ ├── 60-structural-typing │ │ ├── Makefile │ │ ├── assets │ │ │ ├── structual_object.png │ │ │ └── object_intersection.png │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── test.ts │ │ ├── index.ts │ │ └── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ ├── description.en.yml │ └── description.ru.yml ├── 50-objects │ ├── 15-object │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 45-record │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 20-index-signatures │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── test.ts │ │ └── index.ts │ ├── 30-mapped-types │ │ ├── Makefile │ │ ├── index.ts │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ └── test.ts │ ├── 35-mapping-modifiers │ │ ├── Makefile │ │ ├── index.ts │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ └── test.ts │ ├── description.ru.yml │ └── description.en.yml ├── 30-classes │ ├── 10-class-fields │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── test.ts │ │ └── index.ts │ ├── 15-class-as-types │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── test.ts │ │ └── index.ts │ ├── 30-class-extending │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 70-static-property │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 80-abstract-classes │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── README.md │ │ ├── ru │ │ │ └── data.yml │ │ ├── index.ts │ │ └── test.ts │ ├── 20-members-visibility │ │ ├── Makefile │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 23-parameter-properties │ │ ├── Makefile │ │ ├── index.ts │ │ ├── en │ │ │ ├── data.yml │ │ │ ├── EXERCISE.md │ │ │ └── README.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ ├── EXERCISE.md │ │ │ └── README.md │ │ └── test.ts │ ├── description.en.yml │ └── description.ru.yml ├── 40-generics │ ├── 20-generic-types │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ └── test.ts │ ├── 40-many-parameters │ │ ├── Makefile │ │ ├── index.ts │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ └── test.ts │ ├── 50-async-functions │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 60-generic-classes │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ └── test.ts │ ├── 10-generics-overview │ │ ├── Makefile │ │ ├── index.ts │ │ ├── ru │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ ├── en │ │ │ ├── EXERCISE.md │ │ │ └── data.yml │ │ └── test.ts │ ├── 30-generic-functions │ │ ├── Makefile │ │ ├── en │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ ├── index.ts │ │ ├── ru │ │ │ ├── data.yml │ │ │ └── EXERCISE.md │ │ └── test.ts │ ├── description.en.yml │ └── description.ru.yml └── 35-interfaces │ ├── 20-interface-using │ ├── Makefile │ ├── en │ │ ├── data.yml │ │ └── EXERCISE.md │ ├── ru │ │ ├── data.yml │ │ └── EXERCISE.md │ ├── index.ts │ └── test.ts │ ├── 10-interfaces-overview │ ├── Makefile │ ├── ru │ │ ├── data.yml │ │ └── EXERCISE.md │ ├── en │ │ ├── data.yml │ │ └── EXERCISE.md │ ├── test.ts │ └── index.ts │ ├── 30-interface-implementation │ ├── Makefile │ ├── ru │ │ ├── data.yml │ │ └── EXERCISE.md │ ├── en │ │ ├── data.yml │ │ └── EXERCISE.md │ ├── test.ts │ └── index.ts │ ├── description.en.yml │ └── description.ru.yml ├── .gitignore ├── bin └── test2.sh ├── docker-compose.yml ├── .languagetool.cfg ├── .yamllint ├── docker-compose.override.yml ├── vitest.config.ts ├── spec.yml ├── package.json ├── Dockerfile ├── tsconfig.json ├── .github └── workflows │ └── Docker.yml ├── description.en.yml ├── description.ru.yml ├── README.md ├── Makefile └── biome.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/55-objects/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/60-enums/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/80-any/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/90-modules/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/20-functions/30-void/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/22-arrays/50-tuples/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/22-nullable/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/50-objects/15-object/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/50-objects/45-record/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/30-variables/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/70-type-aliases/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/20-functions/35-never/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/20-functions/40-unknown/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/25-literal-types/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/40-assignability/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/70-variability/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/40-named-functions/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/55-objects/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Object Types 3 | -------------------------------------------------------------------------------- /modules/20-functions/40-unknown/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Тип unknown 3 | -------------------------------------------------------------------------------- /modules/20-functions/70-destructuring/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/20-functions/80-rest-spread/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/20-functions/90-type-narrowing/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/22-arrays/20-type-annotations/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/22-arrays/40-readonly-arrays/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Union Types 3 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/30-classes/10-class-fields/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/30-classes/15-class-as-types/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/30-classes/30-class-extending/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/30-classes/70-static-property/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/30-classes/80-abstract-classes/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/20-generic-types/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/40-many-parameters/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/50-async-functions/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/60-generic-classes/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/50-objects/15-object/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Type 'object' 3 | -------------------------------------------------------------------------------- /modules/50-objects/15-object/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Тип object 3 | -------------------------------------------------------------------------------- /modules/50-objects/20-index-signatures/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/50-objects/30-mapped-types/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/index.ts: -------------------------------------------------------------------------------- 1 | console.log('Hello, World!'); 2 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/70-type-aliases/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Type Aliases 3 | -------------------------------------------------------------------------------- /modules/20-functions/40-unknown/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Unknown Type 3 | -------------------------------------------------------------------------------- /modules/20-functions/85-function-overloads/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Types as sets 3 | -------------------------------------------------------------------------------- /modules/30-classes/20-members-visibility/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/35-interfaces/20-interface-using/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/10-generics-overview/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/30-generic-functions/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/50-objects/35-mapping-modifiers/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /**/logs/ 2 | *.DS_Store 3 | node_modules 4 | .vscode 5 | .idea/ 6 | -------------------------------------------------------------------------------- /modules/10-basics/40-named-functions/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Named functions 3 | -------------------------------------------------------------------------------- /modules/20-functions/10-function-as-parameter/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/20-functions/80-rest-spread/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Rest and Spread 3 | -------------------------------------------------------------------------------- /modules/20-functions/80-rest-spread/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Rest и Spread 3 | -------------------------------------------------------------------------------- /modules/22-arrays/25-multi-dimensional-arrays/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Типы как множества 3 | -------------------------------------------------------------------------------- /modules/35-interfaces/10-interfaces-overview/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/20-typescript-as-a-second-language/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/10-basics/40-named-functions/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Именованные функции 3 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Анонимные функции 3 | -------------------------------------------------------------------------------- /modules/10-basics/55-objects/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Объектные типы (Object Types) 3 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Объединения (Union Types) 3 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Intersections Types 3 | -------------------------------------------------------------------------------- /modules/35-interfaces/30-interface-implementation/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/60-generic-classes/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Generics in Classes 3 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Anonymous functions 3 | -------------------------------------------------------------------------------- /modules/20-functions/20-optional-parameters-in-callbacks/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @ test2.sh 3 | -------------------------------------------------------------------------------- /modules/40-generics/60-generic-classes/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Дженерики на классах 3 | -------------------------------------------------------------------------------- /bin/test2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FORCE_COLOR=1 vitest related --run `pwd`/test.ts --no-cache 4 | -------------------------------------------------------------------------------- /modules/10-basics/70-type-aliases/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Псевдонимы типов (Type Aliases) 3 | -------------------------------------------------------------------------------- /modules/22-arrays/25-multi-dimensional-arrays/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Многомерные массивы 3 | -------------------------------------------------------------------------------- /modules/30-classes/20-members-visibility/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Защита свойств и методов 3 | -------------------------------------------------------------------------------- /modules/30-classes/70-static-property/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Static methods and properties 3 | -------------------------------------------------------------------------------- /modules/30-classes/70-static-property/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Статические методы и свойства 3 | -------------------------------------------------------------------------------- /modules/22-arrays/25-multi-dimensional-arrays/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Multidimensional arrays 3 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Пересечение (Intersections Types) 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | services: 4 | exercises: 5 | build: . 6 | command: make check 7 | -------------------------------------------------------------------------------- /modules/30-classes/20-members-visibility/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Protecting properties and methods 3 | -------------------------------------------------------------------------------- /modules/35-interfaces/30-interface-implementation/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Реализация интерфейсов классами 3 | -------------------------------------------------------------------------------- /modules/35-interfaces/30-interface-implementation/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Implementing interfaces with classes 3 | -------------------------------------------------------------------------------- /modules/20-functions/20-optional-parameters-in-callbacks/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Optional parameters in functions 3 | -------------------------------------------------------------------------------- /modules/20-functions/20-optional-parameters-in-callbacks/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Опциональные параметры в функциях 3 | -------------------------------------------------------------------------------- /.languagetool.cfg: -------------------------------------------------------------------------------- 1 | disabledRules=EN_COMPOUNDS_PRE_CLASS,ENGLISH_WORD_REPEAT_RULE,EN_UNPAIRED_QUOTES,LC_AFTER_PERIOD[1],ID_CASING[2] 2 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Напишите функцию, которая возвращает массив четных чисел из массива `numbers`. 3 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Write a function that returns an array of even numbers from the array `numbers`. 3 | -------------------------------------------------------------------------------- /modules/20-functions/35-never/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a `fail()` function that throws any exception. Write its return type explicitly. 3 | -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type CustomType = null | undefined | number; 3 | // END 4 | 5 | export default CustomType; 6 | -------------------------------------------------------------------------------- /modules/20-functions/35-never/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function fail(): never { 3 | throw new Error('wow'); 4 | } 5 | // END 6 | 7 | export default fail; 8 | -------------------------------------------------------------------------------- /modules/20-functions/35-never/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `fail()`, которая выбрасывает любое исключение. Пропишете ее возвращаемый тип явно. 3 | -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Define `CustomType`, which can take these values: 3 | 4 | 1. null 5 | 2. undefined 6 | 3. number 7 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | extends: default 4 | 5 | rules: 6 | line-length: disable 7 | empty-lines: disable 8 | trailing-spaces: disable 9 | 10 | -------------------------------------------------------------------------------- /modules/20-functions/35-never/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Never Type 3 | tips: 4 | - | 5 | [Never Type](https://basarat.gitbook.io/typescript/type-system/never) 6 | -------------------------------------------------------------------------------- /docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | services: 4 | exercises: 5 | volumes: 6 | - .:/exercises-typescript 7 | environment: 8 | - CHECK_TESTS=1 9 | -------------------------------------------------------------------------------- /modules/10-basics/60-enums/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enums 3 | tips: 4 | - | 5 | [Official documentation](https://www.typescriptlang.org/docs/handbook/enums.html) 6 | -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/assets/some_type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexlet-basics/exercises-typescript/HEAD/modules/25-types/10-type-as-sets/assets/some_type.png -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Определите тип `CustomType`, который может принимать следующие значения: 3 | 4 | 1. null 5 | 2. undefined 6 | 3. числа 7 | -------------------------------------------------------------------------------- /modules/20-functions/35-never/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Тип never (возврат из функции) 3 | tips: 4 | - | 5 | [Never Type](https://basarat.gitbook.io/typescript/type-system/never) 6 | -------------------------------------------------------------------------------- /modules/20-functions/35-never/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import fail from './index'; 3 | 4 | test('function', () => { 5 | expect(fail).toThrow(); 6 | }); 7 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/assets/number_or_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexlet-basics/exercises-typescript/HEAD/modules/25-types/20-union-types/assets/number_or_string.png -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Наберите в редакторе код из задания символ в символ и нажмите «Проверить». 2 | 3 | ```typescript 4 | console.log('Hello, World!'); 5 | ``` 6 | -------------------------------------------------------------------------------- /modules/10-basics/20-typescript-as-a-second-language/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function multiply(a: number, b: number) { 3 | return a * b; 4 | } 5 | // END 6 | 7 | export default multiply; 8 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Arrays 3 | tips: 4 | - | 5 | [Official documentation](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#arrays) 6 | -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/assets/hierarcy_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexlet-basics/exercises-typescript/HEAD/modules/25-types/50-type-hierarcy/assets/hierarcy_circle.png -------------------------------------------------------------------------------- /modules/10-basics/60-enums/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Перечисления (Enums) 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/enums.html) 7 | -------------------------------------------------------------------------------- /modules/10-basics/80-any/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Type Any 3 | tips: 4 | - | 5 | [Official documentation about any](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any) 6 | -------------------------------------------------------------------------------- /modules/25-types/25-literal-types/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Literal Types 3 | tips: 4 | - > 5 | [Literal 6 | Types](https://basarat.gitbook.io/typescript/type-system/literal-types) 7 | -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Type hierarchy 3 | tips: 4 | - > 5 | [Type 6 | Assertion](https://basarat.gitbook.io/typescript/type-system/type-assertion) 7 | -------------------------------------------------------------------------------- /modules/30-classes/10-class-fields/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Classes 3 | tips: 4 | - > 5 | [Official 6 | documentation](https://www.typescriptlang.org/docs/handbook/2/classes.html) 7 | -------------------------------------------------------------------------------- /modules/30-classes/10-class-fields/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Классы 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/2/classes.html) 7 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Массивы 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#arrays) 7 | -------------------------------------------------------------------------------- /modules/20-functions/80-rest-spread/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function max(first: number, ...rest: number[]): number { 3 | return Math.max(first, ...rest); 4 | } 5 | // END 6 | 7 | export default max; 8 | -------------------------------------------------------------------------------- /modules/22-arrays/50-tuples/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Tuples 3 | tips: 4 | - | 5 | [Official documentation about tuples](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) 6 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/assets/structual_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexlet-basics/exercises-typescript/HEAD/modules/25-types/60-structural-typing/assets/structual_object.png -------------------------------------------------------------------------------- /modules/35-interfaces/20-interface-using/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Using interfaces 3 | tips: 4 | - | 5 | [Official documentation](https://www.typescriptlang.org/docs/handbook/2/objects.html) 6 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Copy the exact code from the instructions into the editor and execute it by clicking “Run”. 2 | 3 | ```typescript 4 | console.log('Hello, World!'); 5 | ``` 6 | -------------------------------------------------------------------------------- /modules/10-basics/80-any/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Тип Any 3 | tips: 4 | - > 5 | [Официальная документация про 6 | any](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any) 7 | -------------------------------------------------------------------------------- /modules/25-types/25-literal-types/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Литералы (Literal Types) 3 | tips: 4 | - > 5 | [Literal 6 | Types](https://basarat.gitbook.io/typescript/type-system/literal-types) 7 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/assets/one_hundred_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexlet-basics/exercises-typescript/HEAD/modules/25-types/30-intersection-types/assets/one_hundred_order.png -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/assets/object_intersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexlet-basics/exercises-typescript/HEAD/modules/25-types/60-structural-typing/assets/object_intersection.png -------------------------------------------------------------------------------- /modules/35-interfaces/10-interfaces-overview/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Об интерфейсах 3 | tips: 4 | - > 5 | [Interfaces vs 6 | Types](https://ultimatecourses.com/blog/typescript-interfaces-vs-types) 7 | -------------------------------------------------------------------------------- /modules/40-generics/30-generic-functions/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Generics (Functions) 3 | tips: 4 | - | 5 | [TypeScript Generic Functions](https://www.geeksforgeeks.org/typescript-generic-functions/) 6 | -------------------------------------------------------------------------------- /modules/20-functions/70-destructuring/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function lessonsCount({ lessons }: { lessons: string[] }): number { 3 | return lessons.length; 4 | } 5 | // END 6 | 7 | export default lessonsCount; 8 | -------------------------------------------------------------------------------- /modules/30-classes/15-class-as-types/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Classes as types 3 | tips: 4 | - > 5 | [Constructor 6 | overload](https://www.typescriptlang.org/docs/handbook/2/classes.html#constructors) 7 | -------------------------------------------------------------------------------- /modules/30-classes/15-class-as-types/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Классы как типы 3 | tips: 4 | - > 5 | [Перегрузка 6 | конструктора](https://www.typescriptlang.org/docs/handbook/2/classes.html#constructors) 7 | -------------------------------------------------------------------------------- /modules/35-interfaces/10-interfaces-overview/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: About interfaces 3 | tips: 4 | - > 5 | [Interfaces vs 6 | Types](https://ultimatecourses.com/blog/typescript-interfaces-vs-types) 7 | -------------------------------------------------------------------------------- /modules/50-objects/45-record/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Records 3 | tips: 4 | - > 5 | [Record in Official 6 | Documentation](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) 7 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/*.test.ts', '**/*.spec.ts', '**/test.ts'], 6 | }, 7 | // cacheDir: false 8 | }); 9 | -------------------------------------------------------------------------------- /modules/20-functions/10-function-as-parameter/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Функции как параметры 3 | tips: 4 | - > 5 | [О функциях в The TypeScript 6 | Handbook](https://habr.com/ru/company/macloud/blog/561470/) 7 | -------------------------------------------------------------------------------- /modules/22-arrays/50-tuples/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Кортежи (Tuples) 3 | tips: 4 | - > 5 | [Кортежи в официальной 6 | документации](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) 7 | -------------------------------------------------------------------------------- /modules/35-interfaces/20-interface-using/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Использование интерфейсов 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/2/objects.html) 7 | -------------------------------------------------------------------------------- /modules/40-generics/10-generics-overview/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function last(coll: Array): T | null { 3 | return coll.length > 0 ? coll[coll.length - 1] : null; 4 | } 5 | // END 6 | 7 | export default last; 8 | -------------------------------------------------------------------------------- /modules/40-generics/20-generic-types/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Generics (Types) 3 | tips: 4 | - > 5 | [Generic 6 | constraints](https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints) 7 | -------------------------------------------------------------------------------- /modules/40-generics/20-generic-types/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type MySet = { 3 | items: Array; 4 | has(value: T): boolean; 5 | add(value: T): number; 6 | }; 7 | // END 8 | 9 | export default MySet; 10 | -------------------------------------------------------------------------------- /modules/10-basics/55-objects/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function isComplete(course: { name: string; lessons: string[] }): boolean { 3 | return course.lessons.length >= 4; 4 | } 5 | // END 6 | 7 | export default isComplete; 8 | -------------------------------------------------------------------------------- /modules/20-functions/40-unknown/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function isPlainObject(value: unknown): boolean { 3 | return value instanceof Object && !Array.isArray(value); 4 | } 5 | // END 6 | 7 | export default isPlainObject; 8 | -------------------------------------------------------------------------------- /modules/20-functions/85-function-overloads/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Function Overloads 3 | tips: 4 | - | 5 | [Official documentation](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads) 6 | -------------------------------------------------------------------------------- /modules/50-objects/45-record/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Записи (Record) 3 | tips: 4 | - > 5 | [Record в официальной 6 | документации](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) 7 | -------------------------------------------------------------------------------- /modules/20-functions/70-destructuring/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Destructuring 3 | tips: 4 | - > 5 | [Official 6 | documentation](https://www.typescriptlang.org/docs/handbook/2/functions.html#parameter-destructuring) 7 | -------------------------------------------------------------------------------- /modules/22-arrays/20-type-annotations/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Type annotations 3 | tips: 4 | - > 5 | [Official documentation 6 | about arrays](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#arrays) 7 | -------------------------------------------------------------------------------- /modules/22-arrays/20-type-annotations/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Аннотации типов 3 | tips: 4 | - > 5 | [Массивы в официальной 6 | документации](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#arrays) 7 | -------------------------------------------------------------------------------- /modules/20-functions/10-function-as-parameter/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Functions as parameters 3 | tips: 4 | - > 5 | [Functions in The TypeScript 6 | Handbook](https://www.typescriptlang.org/docs/handbook/2/functions.html) 7 | -------------------------------------------------------------------------------- /modules/20-functions/70-destructuring/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Деструктуризация 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/2/functions.html#parameter-destructuring) 7 | -------------------------------------------------------------------------------- /modules/22-arrays/40-readonly-arrays/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Readonly arrays 3 | tips: 4 | - | 5 | [Read-Only Array and Tuple Types in TypeScript](https://mariusschulz.com/blog/read-only-array-and-tuple-types-in-typescript/) 6 | -------------------------------------------------------------------------------- /modules/22-arrays/40-readonly-arrays/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function reverse(coll: readonly number[]): number[] { 3 | return coll.map((_, index) => coll[coll.length - 1 - index]); 4 | } 5 | // END 6 | 7 | export default reverse; 8 | -------------------------------------------------------------------------------- /modules/40-generics/40-many-parameters/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type MyMap = { 3 | values: Map; 4 | set(key: K, value: V): void; 5 | get(key: K): V | undefined; 6 | }; 7 | // END 8 | 9 | export default MyMap; 10 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/index.ts: -------------------------------------------------------------------------------- 1 | const numbers = [1, 3, 8, 9, 100, 23, 55, 34]; 2 | 3 | // BEGIN 4 | const getEvenNumbers = () => numbers.filter((num) => num % 2 === 0); 5 | // END 6 | 7 | export default getEvenNumbers; 8 | -------------------------------------------------------------------------------- /modules/40-generics/40-many-parameters/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Generics with multiple parameters 3 | tips: 4 | - > 5 | [Typescript Generics 6 | Explained](https://rossbulat.medium.com/typescript-generics-explained-15c6493b510f) 7 | -------------------------------------------------------------------------------- /modules/22-arrays/40-readonly-arrays/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Массивы только для чтения 3 | tips: 4 | - > 5 | [Массивы только для чтения и 6 | кортежи](https://mariusschulz.com/blog/read-only-array-and-tuple-types-in-typescript/) 7 | -------------------------------------------------------------------------------- /modules/40-generics/40-many-parameters/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Дженерики с несколькими параметрами 3 | tips: 4 | - > 5 | [Typescript Generics 6 | Explained](https://rossbulat.medium.com/typescript-generics-explained-15c6493b510f) 7 | -------------------------------------------------------------------------------- /modules/10-basics/20-typescript-as-a-second-language/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | По образцу примера с суммой из урока напишите функцию, которая находит произведение переданных чисел: 2 | 3 | ```typescript 4 | multiply(3, 8); // 24 5 | multiply(1, 2); // 2 6 | ``` 7 | -------------------------------------------------------------------------------- /modules/20-functions/85-function-overloads/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Перегрузка функций (Function Overloads) 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads) 7 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function lastIndex(str: string, char: string): number | null { 3 | const index = str.lastIndexOf(char); 4 | return index === -1 ? null : index; 5 | } 6 | // END 7 | 8 | export default lastIndex; 9 | -------------------------------------------------------------------------------- /modules/35-interfaces/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Interfaces 3 | description: | 4 | Interfaces in TypeScript allow you to describe object types differently in the syntax familiar to OOP languages and extend the semantics - adding useful new uses. 5 | -------------------------------------------------------------------------------- /modules/10-basics/20-typescript-as-a-second-language/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Using the sum example from the lesson, write a function that finds the product of the given numbers: 3 | 4 | ```typescript 5 | multiply(3, 8); // 24 6 | multiply(1, 2); // 2 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/20-functions/80-rest-spread/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import max from './index'; 3 | 4 | test('function', () => { 5 | expect(max(1, 3, 8)).toBe(8); 6 | expect(max(10)).toBe(10); 7 | expect(max(4, 1)).toBe(4); 8 | }); 9 | -------------------------------------------------------------------------------- /modules/20-functions/80-rest-spread/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Определите функцию `max()`, которая отличается от примера из урока только тем, что у нее первый параметр обязательный. 3 | 4 | Например: 5 | 6 | ```typescript 7 | max(1,2,3); 8 | max(234); 9 | ``` 10 | -------------------------------------------------------------------------------- /modules/20-functions/80-rest-spread/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Define the `max()` function, which differs from the example in the lesson only in that its first parameter is mandatory. 3 | 4 | For example: 5 | 6 | ```typescript 7 | max(1,2,3); 8 | max(234); 9 | ``` 10 | -------------------------------------------------------------------------------- /modules/35-interfaces/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Интерфейсы 3 | description: | 4 | Интерфейсы в TypeScript позволяют по-другому описывать объектные типы в привычном для ООП языков синтаксисе и расширяют семантику — добавляют новые полезные возможности использования. 5 | -------------------------------------------------------------------------------- /modules/22-arrays/50-tuples/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | export type Point = [number, number, number]; 3 | 4 | function isTheSamePoint(p1: Point, p2: Point): boolean { 5 | return p1.every((el, i) => el === p2[i]); 6 | } 7 | // END 8 | 9 | export default isTheSamePoint; 10 | -------------------------------------------------------------------------------- /modules/20-functions/30-void/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | 3 | function forEach( 4 | numbers: number[], 5 | callback: (n: number, index: number) => void, 6 | ): void { 7 | numbers.forEach((n, index) => callback(n, index)); 8 | } 9 | // END 10 | 11 | export default forEach; 12 | -------------------------------------------------------------------------------- /modules/40-generics/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Generics 3 | description: | 4 | Generics allow you to write more versatile code that can work with different data types. TypeScript has a powerful generics system that allows you to create flexible yet robust parts of the system. 5 | -------------------------------------------------------------------------------- /modules/10-basics/30-variables/index.ts: -------------------------------------------------------------------------------- 1 | function repeat(text: string, count: number) { 2 | // BEGIN 3 | let result = ''; 4 | for (let i = 0; i < count; i += 1) { 5 | result += text; 6 | } 7 | 8 | return result; 9 | // END 10 | } 11 | 12 | export default repeat; 13 | -------------------------------------------------------------------------------- /modules/20-functions/90-type-narrowing/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Type Narrowing 3 | tips: 4 | - | 5 | [Official documentation](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) 6 | - | 7 | [Type Guard](https://basarat.gitbook.io/typescript/type-system/typeguard) 8 | -------------------------------------------------------------------------------- /modules/30-classes/30-class-extending/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Inheritance 3 | tips: 4 | - | 5 | [Class inheritance](https://www.typescriptlang.org/docs/handbook/2/classes.html#extends-clauses) 6 | - | 7 | [Mixins](https://basarat.gitbook.io/typescript/type-system/mixins) 8 | -------------------------------------------------------------------------------- /spec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | language: 4 | docker_image: "ghcr.io/hexlet-basics/exercises-typescript" 5 | extension: ts 6 | exercise_filename: index.ts 7 | exercise_test_filename: test.ts 8 | learn_as: second_language 9 | progress: in_development 10 | name: TypeScript 11 | -------------------------------------------------------------------------------- /modules/20-functions/30-void/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Type Void 3 | tips: 4 | - | 5 | [Official documentation](https://www.typescriptlang.org/docs/handbook/basic-types.html#void) 6 | - | 7 | [void in TypeScript and JavaScript](https://fettblog.eu/void-in-javascript-and-typescript/) 8 | -------------------------------------------------------------------------------- /modules/20-functions/90-type-narrowing/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function last(value: string | number): string | number { 3 | if (typeof value === 'number') { 4 | return value % 10; 5 | } 6 | 7 | return value[value.length - 1] ?? ''; 8 | } 9 | // END 10 | 11 | export default last; 12 | -------------------------------------------------------------------------------- /modules/22-arrays/40-readonly-arrays/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import reverse from './index'; 3 | 4 | test('function', () => { 5 | expect(reverse([])).toEqual([]); 6 | expect(reverse([1, 2])).toEqual([2, 1]); 7 | expect(reverse([8, 3, 9])).toEqual([9, 3, 8]); 8 | }); 9 | -------------------------------------------------------------------------------- /modules/25-types/22-nullable/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function formatPrice(price?: number | null): string { 3 | if (price === undefined || price === null) { 4 | return '$0.00'; 5 | } 6 | 7 | return `$${price.toFixed(2)}`; 8 | } 9 | // END 10 | 11 | export default formatPrice; 12 | -------------------------------------------------------------------------------- /modules/40-generics/10-generics-overview/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте дженерик `last()`, который извлекает последний элемент из массива если он есть или null — если его нет: 3 | 4 | ```typescript 5 | last([]); // null 6 | last([3, 2]); // 2 7 | last(['code-basics', 'hexlet']); // hexlet 8 | ``` 9 | -------------------------------------------------------------------------------- /modules/40-generics/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Дженерики 3 | description: | 4 | Дженерики позволяют писать более универсальный код, который может работать с разными типами данных. В TypeScript мощная система дженериков, которая позволяет создавать гибкие и при этом надежные части системы. 5 | -------------------------------------------------------------------------------- /modules/20-functions/90-type-narrowing/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import last from './index'; 3 | 4 | test('function', () => { 5 | expect(last('John')).toBe('n'); 6 | expect(last('')).toBe(''); 7 | expect(last(1)).toBe(1); 8 | expect(last(134)).toBe(4); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/22-arrays/25-multi-dimensional-arrays/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function getField(size: number): null[][] { 3 | const field = Array(size) 4 | .fill(null) 5 | .map(() => Array(size).fill(null)); 6 | return field; 7 | } 8 | // END 9 | 10 | export default getField; 11 | -------------------------------------------------------------------------------- /modules/40-generics/10-generics-overview/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a generic `last()` that retrieves the last element from the array if there is one, or null if there is none: 3 | 4 | ```typescript 5 | last([]); // null 6 | last([3, 2]); // 2 7 | last(['code-basics', 'hexlet']); // hexlet 8 | ``` 9 | -------------------------------------------------------------------------------- /modules/50-objects/15-object/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | const extract = (obj: object, keys: Array): object => { 3 | const entries = Object.entries(obj).filter(([key]) => keys.includes(key)); 4 | 5 | return Object.fromEntries(entries); 6 | }; 7 | // END 8 | 9 | export default extract; 10 | -------------------------------------------------------------------------------- /modules/50-objects/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Типизация объектов 4 | description: | 5 | Объекты в TypeScript можно описать с помощью интерфейсов и типов. В этом разделе мы рассмотрим особенности типизации объектов, получением динамических свойств и особенностями определения объектных типов. 6 | -------------------------------------------------------------------------------- /modules/10-basics/20-typescript-as-a-second-language/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import multiply from './index'; 4 | 5 | test('multiply', () => { 6 | expect(multiply(1, 3)).toBe(3); 7 | 8 | expectTypeOf(multiply).returns.toExtend(); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/20-functions/70-destructuring/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import lessonsCount from './index'; 3 | 4 | test('function', () => { 5 | expect(lessonsCount({ lessons: [] })).toBe(0); 6 | const course = { lessons: ['intro'] }; 7 | expect(lessonsCount(course)).toBe(1); 8 | }); 9 | -------------------------------------------------------------------------------- /modules/20-functions/85-function-overloads/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import sayHello from './index'; 3 | 4 | test('function', () => { 5 | expect(sayHello('John')).toBe('Hi John! Happy New Year!'); 6 | expect(sayHello(2023, 'Mila')).toBe('Hi Mila! Happy New Year 2023!'); 7 | }); 8 | -------------------------------------------------------------------------------- /modules/20-functions/90-type-narrowing/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Сужение типа (Narrowing) 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) 7 | - | 8 | [Type Guard](https://basarat.gitbook.io/typescript/type-system/typeguard) 9 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Реализуйте функцию `lastIndex(str, char)`, которая возвращает индекс последнего вхождения символа в строку или `null`, если такого символа нет. 2 | 3 | ```typescript 4 | const str = 'test'; 5 | lastIndex(str, 't'); // 3 6 | lastIndex(str, 'p'); // null 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/40-generics/30-generic-functions/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type MyArray = { 3 | items: Array; 4 | push(value: T): number; 5 | filter( 6 | callback: (value: T, index: number, array: Array) => boolean, 7 | ): MyArray; 8 | }; 9 | // END 10 | 11 | export default MyArray; 12 | -------------------------------------------------------------------------------- /modules/10-basics/40-named-functions/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function getHiddenCard(cardNumber: string, hiddenPartLength = 4): string { 3 | const visibleDigitsLine = cardNumber.slice(-4); 4 | return visibleDigitsLine.padStart(hiddenPartLength + 4, '*'); 5 | } 6 | // END 7 | 8 | export default getHiddenCard; 9 | -------------------------------------------------------------------------------- /modules/20-functions/30-void/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Тип Void 3 | tips: 4 | - > 5 | [Официальная 6 | документация](https://www.typescriptlang.org/docs/handbook/basic-types.html#void) 7 | - > 8 | [void в TypeScript и 9 | JavaScript](https://habr.com/ru/company/ruvds/blog/468229/) 10 | -------------------------------------------------------------------------------- /modules/20-functions/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Function typing 3 | description: | 4 | Most of the tasks of function typing are related to the correct description of input parameters and return values, which can be quite complex. This also includes peculiarities of working with rest and destructuring. 5 | -------------------------------------------------------------------------------- /modules/20-functions/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Типизация функций 3 | description: | 4 | Большая часть задач по типизации функций связана с правильным описанием входных параметров и возвращаемых значений, которые могут быть достаточно сложными. Сюда же входят особенности работы с rest и destructuring. 5 | -------------------------------------------------------------------------------- /modules/22-arrays/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Array typing 3 | description: | 4 | Working with arrays in some cases requires more precise typing. In this module, we will look at the features of array typing: arrays containing elements of different types, multidimensional arrays, and fixed-length arrays. 5 | -------------------------------------------------------------------------------- /modules/22-arrays/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Типизация массивов 3 | description: | 4 | Работа с массивами в некоторых случаях требует более точной типизации. В этом модуле мы рассмотрим особенности типизации массивов: содержащие элементы разных типов, многомерные массивы и массивы фиксированной длины. 5 | -------------------------------------------------------------------------------- /modules/30-classes/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Classes 3 | description: | 4 | Classes are used to describe identical objects with state and behavior. Classes in TypeScript extend the standard ones in JavaScript and provide additional tools that are so popular with programmers in "classic" OOP languages. 5 | -------------------------------------------------------------------------------- /modules/50-objects/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Object Typing 4 | description: | 5 | Objects in TypeScript can be described using interfaces and types. In this section, we will look at the peculiarities of typing objects, obtaining dynamic properties, and the peculiarities of defining object types. 6 | -------------------------------------------------------------------------------- /modules/20-functions/85-function-overloads/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `newYearCongratulate()`, которая аналогична примеру на Kotlin из теории: 3 | 4 | ```typescript 5 | newYearCongratulate('John'); // Hi John! Happy New Year! 6 | newYearCongratulate(2023, 'Mila'); // Hi Mila! Happy New Year 2023! 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/25-types/40-assignability/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Реализуйте объект по описанному типу `Form`. Поле `name.value` должно проходить валидацию, а поле `age` — нет. 2 | 3 | ```typescript 4 | console.log(form.name.validator(form.name.value)); // true 5 | console.log(form.age.validator(form.age.value)); // false 6 | ``` 7 | -------------------------------------------------------------------------------- /modules/30-classes/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Классы 3 | description: | 4 | С помощью классов описывают одинаковые объекты с состоянием и поведением. Классы в TypeScript расширяют стандартные в JavaScript и предоставляют дополнительные инструменты, так полюбившиеся программистам на "классических" ООП языках. 5 | -------------------------------------------------------------------------------- /modules/10-basics/30-variables/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Variables 3 | tips: 4 | - | 5 | [Method String.repeat()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) 6 | - | 7 | [Official documentation](https://www.typescriptlang.org/docs/handbook/2/basic-types.html) 8 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import getEvenNumbers from './index'; 4 | 5 | test('function', () => { 6 | expect(getEvenNumbers()).toEqual([8, 100, 34]); 7 | 8 | expectTypeOf(getEvenNumbers).returns.toExtend(); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function filterAnagrams(anagram: string, anagrams: string[]): string[] { 3 | const standard = anagram.split('').sort().join(''); 4 | return anagrams.filter((item) => item.split('').sort().join('') === standard); 5 | } 6 | // END 7 | 8 | export default filterAnagrams; 9 | -------------------------------------------------------------------------------- /modules/25-types/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: More about types 3 | description: | 4 | Combining types using intersection and union, their peculiarities. In this module, we'll cover the basic concepts that allow you to gain a deeper understanding of how typing works in TypeScript and not be afraid of compiler errors. 5 | -------------------------------------------------------------------------------- /modules/30-classes/70-static-property/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | class UserResponse { 3 | constructor(public user: string) {} 4 | 5 | static fromArray(users: string[]): UserResponse[] { 6 | return users.map((user) => new UserResponse(user)); 7 | } 8 | } 9 | // END 10 | 11 | export default UserResponse; 12 | -------------------------------------------------------------------------------- /modules/40-generics/30-generic-functions/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Дженерики (Функции) 3 | tips: 4 | - > 5 | [Дженерики в TypeScript: разбираемся 6 | вместе](https://habr.com/ru/post/455473/) 7 | - > 8 | [Полиморфизм простыми 9 | словами](https://medium.com/devschacht/polymorphism-207d9f9cd78) 10 | -------------------------------------------------------------------------------- /modules/20-functions/85-function-overloads/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `newYearCongratulate()` function, which is similar to the Kotlin example from theory: 3 | 4 | ```typescript 5 | newYearCongratulate('John'); // Hi John! Happy New Year! 6 | newYearCongratulate(2023, 'Mila'); // Hi Mila! Happy New Year 2023! 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | class CustomFile { 3 | constructor( 4 | private name: string, 5 | private size: number, 6 | ) {} 7 | 8 | toString() { 9 | return `${this.name} (${this.size} bytes)`; 10 | } 11 | } 12 | // END 13 | 14 | export default CustomFile; 15 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `lastIndex(str, char)` function that returns the index of the last occurrence of a character in the string or `null` if there is no such character. 3 | 4 | ```typescript 5 | const str = 'test'; 6 | lastIndex(str, 't'); // 3 7 | lastIndex(str, 'p'); // null 8 | ``` 9 | -------------------------------------------------------------------------------- /modules/25-types/40-assignability/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Implement an object of the described `Form` type. The `name.value` field should be validated, but the `age` field should not. 2 | 3 | ```typescript 4 | console.log(form.name.validator(form.name.value)); // true 5 | console.log(form.age.validator(form.age.value)); // false 6 | ``` 7 | -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Иерархия типов 3 | tips: 4 | - > 5 | [Как устроена система типов в 6 | TypeScript](https://ru.hexlet.io/blog/posts/sistema-tipov-v-typescript) 7 | - > 8 | [Type 9 | Assertion](https://basarat.gitbook.io/typescript/type-system/type-assertion) 10 | -------------------------------------------------------------------------------- /modules/25-types/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Подробнее о типах 3 | description: | 4 | Комбинирование типов с помощью пересечения и объединения, их особенности. В этом модуле мы рассмотрим основные концепции, которые позволяют глубже понять, как работает типизация в TypeScript, и не бояться ошибок, выдаваемых компилятором. 5 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Parameter properties 3 | tips: 4 | - | 5 | [Constructor overload](https://www.typescriptlang.org/docs/handbook/2/classes.html#constructors) 6 | - | 7 | [Typescript Constructor Shorthand](https://dev.to/satansdeer/typescript-constructor-shorthand-3ibd) 8 | -------------------------------------------------------------------------------- /modules/40-generics/50-async-functions/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Asynchronous functions 3 | tips: 4 | - > 5 | [TypeScript Deep Dive: 6 | Promise](https://basarat.gitbook.io/typescript/future-javascript/promise) 7 | - > 8 | [Awaited](https://www.typescriptlang.org/docs/handbook/utility-types.html#awaitedtype) 9 | -------------------------------------------------------------------------------- /modules/35-interfaces/30-interface-implementation/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import Phonebook from './index'; 3 | 4 | test('myBook', () => { 5 | const myBook = new Phonebook(); 6 | myBook.set('test1', 1); 7 | expect(myBook.get('test1')).toBe(1); 8 | expect(myBook.get('test2')).toBe(null); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/10-basics/30-variables/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Переменные 3 | tips: 4 | - > 5 | [Метод 6 | String.repeat()](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) 7 | - > 8 | [Официальная 9 | документация](https://www.typescriptlang.org/docs/handbook/2/basic-types.html) 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hexlet-basics", 3 | "scripts": { 4 | "test": "vitest" 5 | }, 6 | "type": "module", 7 | "devDependencies": { 8 | "@biomejs/biome": "2.1.4", 9 | "@types/node": "^24.2.1", 10 | "npm-check-updates": "^18.0.2", 11 | "typescript": "^5.9.2", 12 | "vitest": "^3.2.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/20-functions/40-unknown/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import isPlainObject from './index'; 3 | 4 | test('function', () => { 5 | expect(isPlainObject(3)).toBe(false); 6 | expect(isPlainObject('hexlet')).toBe(false); 7 | expect(isPlainObject({})).toBe(true); 8 | expect(isPlainObject([])).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/25-types/22-nullable/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import formatPrice from './index'; 3 | 4 | test('formatPrice', () => { 5 | expect(formatPrice()).toBe('$0.00'); 6 | expect(formatPrice(null)).toBe('$0.00'); 7 | expect(formatPrice(200)).toBe('$200.00'); 8 | expect(formatPrice(3.145)).toBe('$3.15'); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Свойства параметров 3 | tips: 4 | - > 5 | [Перегрузка 6 | конструктора](https://www.typescriptlang.org/docs/handbook/2/classes.html#constructors) 7 | - > 8 | [Typescript Constructor 9 | Shorthand](https://dev.to/satansdeer/typescript-constructor-shorthand-3ibd) 10 | -------------------------------------------------------------------------------- /modules/10-basics/30-variables/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Complete the body of the `repeat()` function that repeats the string the specified number of times. The function should return the result. Try not to use built-in methods, you will need a loop for this implementation. 3 | 4 | ```typescript 5 | repeat('hexlet', 2); // hexlethexlet 6 | repeat('wo', 3); // wowowo 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/10-basics/30-variables/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import repeat from './index'; 4 | 5 | test('repeat', () => { 6 | expect(repeat('wow', 3)).toBe('wowwowwow'); 7 | expect(repeat('s', 2)).toBe('ss'); 8 | expect(repeat('s', 0)).toBe(''); 9 | 10 | expectTypeOf(repeat).returns.toExtend(); 11 | }); 12 | -------------------------------------------------------------------------------- /modules/20-functions/10-function-as-parameter/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function filter(numbers: number[], callback: (n: number) => boolean): number[] { 3 | const result: number[] = []; 4 | numbers.forEach((n) => { 5 | if (callback(n)) { 6 | result.push(n); 7 | } 8 | }); 9 | return result; 10 | } 11 | // END 12 | 13 | export default filter; 14 | -------------------------------------------------------------------------------- /modules/20-functions/20-optional-parameters-in-callbacks/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function map( 3 | numbers: number[], 4 | callback: (n: number, index: number) => number, 5 | ): number[] { 6 | const result: number[] = []; 7 | numbers.forEach((n, index) => result.push(callback(n, index))); 8 | return result; 9 | } 10 | // END 11 | 12 | export default map; 13 | -------------------------------------------------------------------------------- /modules/20-functions/70-destructuring/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a `lessonsCount()` function that takes a course as input and returns the number of lectures within it: 3 | 4 | ```typescript 5 | const course = { lessons: ['intro', 'lala'] }; 6 | lessonsCount(course); // 2 7 | ``` 8 | 9 | Use parameter destructuring to extract lessons directly in the function parameters. -------------------------------------------------------------------------------- /modules/50-objects/15-object/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a `extract(object, keys)` function that returns a new object with the specified keys. For example: 3 | 4 | ```typescript 5 | const user = { 6 | name: 'Tirion', 7 | email: 'tirion@lanister.got', 8 | age: 35, 9 | } 10 | 11 | extract(user, ['name', 'age']); // { name: 'Tirion', age: 35 } 12 | ``` 13 | -------------------------------------------------------------------------------- /modules/50-objects/15-object/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `extract(object, keys)`, которая возвращает новый объект с указанными ключами. Например: 3 | 4 | ```typescript 5 | const user = { 6 | name: 'Tirion', 7 | email: 'tirion@lanister.got', 8 | age: 35, 9 | } 10 | 11 | extract(user, ['name', 'age']); // { name: 'Tirion', age: 35 } 12 | ``` 13 | -------------------------------------------------------------------------------- /modules/10-basics/30-variables/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Допишите тело функции `repeat()`, которая повторяет строку указанное количество раз. Функция должна возвращать полученный результат. Постарайтесь не использовать встроенные методы, для такой реализации вам понадобится цикл. 3 | 4 | ```typescript 5 | repeat('hexlet', 2); // hexlethexlet 6 | repeat('wo', 3); // wowowo 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import addAdmin, { Permission, type User } from './index'; 3 | 4 | test('addAdmin', () => { 5 | const user: User = { 6 | login: 'login1', 7 | }; 8 | 9 | const admin = addAdmin(user); 10 | expect(admin).toEqual({ ...user, permission: Permission.READ }); 11 | }); 12 | -------------------------------------------------------------------------------- /modules/30-classes/10-class-fields/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import CustomFile from './index'; 3 | 4 | test('CustomFile', () => { 5 | const file = new CustomFile({ name: 'foo.txt', size: 4 }); 6 | 7 | expect(file.name).toBe('foo.txt'); 8 | expect(file.size).toBe(4); 9 | expect(file.toString()).toBe('foo.txt (4 bytes)'); 10 | }); 11 | -------------------------------------------------------------------------------- /modules/30-classes/30-class-extending/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Наследование 3 | tips: 4 | - > 5 | [Наследование 6 | классов](https://www.typescriptlang.org/docs/handbook/2/classes.html#extends-clauses) 7 | - | 8 | [Вариантность](https://habr.com/ru/post/477448/) 9 | - | 10 | [Mixins](https://basarat.gitbook.io/typescript/type-system/mixins) 11 | -------------------------------------------------------------------------------- /modules/10-basics/60-enums/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | enum ModalStatus { 3 | Opened, 4 | Closed, 5 | } 6 | 7 | function buildModal( 8 | text: string, 9 | status: ModalStatus, 10 | ): { text: string; status: ModalStatus } { 11 | return { 12 | text, 13 | status, 14 | }; 15 | } 16 | // END 17 | 18 | export { ModalStatus }; 19 | export default buildModal; 20 | -------------------------------------------------------------------------------- /modules/10-basics/80-any/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import getParams from './index'; 3 | 4 | test('function', () => { 5 | expect(getParams('per=10&page=5')).toEqual({ per: '10', page: '5' }); 6 | expect(getParams('name=hexlet&count=3&order=asc')).toEqual({ 7 | name: 'hexlet', 8 | count: '3', 9 | order: 'asc', 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /modules/40-generics/10-generics-overview/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import last from './index'; 4 | 5 | test('function', () => { 6 | expect(last([])).toBe(null); 7 | expect(last([3, 4])).toBe(4); 8 | expect(last(['cat', 'dog'])).toBe('dog'); 9 | 10 | expectTypeOf(last).returns.toExtend(); 11 | }); 12 | -------------------------------------------------------------------------------- /modules/20-functions/70-destructuring/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `lessonsCount()`, которая принимает на вход курс и возвращает количество лекций внутри него: 3 | 4 | ```typescript 5 | const course = { lessons: ['intro', 'lala'] }; 6 | lessonsCount(course); // 2 7 | ``` 8 | 9 | Используйте внутри деструктуризацию, чтобы извлечь уроки прямо в параметрах функции. 10 | -------------------------------------------------------------------------------- /modules/22-arrays/20-type-annotations/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function unique(coll: (number | string)[]): (number | string)[] { 3 | const init: (number | string)[] = []; 4 | 5 | return coll.reduce((acc, curr) => { 6 | if (!acc.includes(curr)) { 7 | acc.push(curr); 8 | } 9 | return acc; 10 | }, init); 11 | } 12 | // END 13 | 14 | export default unique; 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hexletbasics/base-image:latest 2 | 3 | ENV PATH=/exercises-typescript/bin:/exercises-typescript/node_modules/.bin:$PATH 4 | # ENV NODE_OPTIONS --max-old-space-size=4096 5 | # --experimental-vm-modules 6 | ENV CI=true 7 | 8 | WORKDIR /exercises-typescript 9 | 10 | RUN npm i -g vitest 11 | COPY package.json package-lock.json ./ 12 | RUN npm ci 13 | 14 | COPY . . 15 | -------------------------------------------------------------------------------- /modules/50-objects/30-mapped-types/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | const sanitize = ( 3 | obj: T, 4 | keys: Array, 5 | ) => { 6 | const entries = Object.entries(obj).filter( 7 | ([key]) => !keys.includes(key as K), 8 | ); 9 | 10 | return Object.fromEntries(entries) as Omit; 11 | }; 12 | // END 13 | 14 | export default sanitize; 15 | -------------------------------------------------------------------------------- /modules/50-objects/45-record/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type Property = string | number | symbol; 3 | 4 | const createAccessChecker = 5 | ( 6 | permissions: Record>, 7 | ) => 8 | (role: Roles, resource: Resource) => 9 | permissions[role].includes(resource); 10 | // END 11 | 12 | export default createAccessChecker; 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "target": "ESNext", 5 | "strict": true, 6 | "lib": [ 7 | "ES2022", 8 | ], 9 | "moduleResolution": "node", 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "forceConsistentCasingInFileNames": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modules/10-basics/70-type-aliases/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `getOlderUser()` function that takes two users as input and returns the one who is older. If the users are of the same age, `null` is returned: 3 | 4 | ```typescript 5 | const user1 = { name: 'Petr', age: 8 }; 6 | ``` 7 | 8 | Define an alias for the user to avoid duplicating the definition of its type in the function parameters. -------------------------------------------------------------------------------- /modules/22-arrays/40-readonly-arrays/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `reverse()` function that flips an array. Technically, it should return a new array with the elements in reverse order. Use the `readonly` modifier for the incoming array. Do not use the built-in `reverse()` method. 3 | 4 | ```typescript 5 | reverse([1, 2, 8]); // [8, 2, 1] 6 | reverse([10, 33, 7, 0]); // [0, 7, 33, 10] 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Hello, World! 3 | tips: 4 | - | 5 | [Repl for experimenting with TypeScript](https://www.typescriptlang.org/play) 6 | - | 7 | [strict in tsconfig](https://www.typescriptlang.org/tsconfig#strict) 8 | - > 9 | [TypeScript compiler options](https://www.typescriptlang.org/docs/handbook/compiler-options.html#compiler-options) 10 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import CustomFile from './index'; 3 | 4 | test('CustomFile', () => { 5 | const file = new CustomFile('foo.txt', 4); 6 | 7 | expect(file.toString()).toBe('foo.txt (4 bytes)'); 8 | 9 | const file2 = new CustomFile('bar.txt', 8); 10 | expect(file2.toString()).toBe('bar.txt (8 bytes)'); 11 | }); 12 | -------------------------------------------------------------------------------- /modules/40-generics/50-async-functions/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Асинхронные функции 3 | tips: 4 | - | 5 | [Что такое Promise](https://doka.guide/js/promise/) 6 | - > 7 | [TypeScript Deep Dive: 8 | Promise](https://basarat.gitbook.io/typescript/future-javascript/promise) 9 | - > 10 | [Awaited](https://www.typescriptlang.org/docs/handbook/utility-types.html#awaitedtype) 11 | -------------------------------------------------------------------------------- /modules/40-generics/60-generic-classes/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | class Queue { 3 | private data: T[] = []; 4 | 5 | enqueue(element: T) { 6 | this.data.push(element); 7 | } 8 | 9 | dequeue() { 10 | if (this.data.length === 0) { 11 | throw new Error('Queue is empty'); 12 | } 13 | return this.data.shift(); 14 | } 15 | } 16 | // END 17 | 18 | export default Queue; 19 | -------------------------------------------------------------------------------- /modules/10-basics/90-modules/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import authorize from './index'; 3 | 4 | test('function', () => { 5 | expect(authorize({ email: 'test@hexlet.io' })).toBe(true); 6 | expect(authorize({ email: 'hexlet.io@example.com' })).toBe(false); 7 | expect(authorize({ email: 'test@example.com' })).toBe(false); 8 | expect(authorize(null)).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/25-types/22-nullable/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `formatPrice()`, которая принимает число и возвращает строку с округлением до второго числа после запятой. Если пришел `null` или `undefined` должна вернуться `'$0.00'`. 3 | 4 | ```typescript 5 | formatPrice(3.145); // '$3.15' 6 | formatPrice(200); // '$200.00' 7 | formatPrice(); // '$0.00' 8 | formatPrice(null); // '$0.00' 9 | ``` 10 | -------------------------------------------------------------------------------- /modules/25-types/22-nullable/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `formatPrice()`, которая принимает число и возвращает строку с округлением до второго числа после запятой. Если пришел `null` или `undefined` должна вернуться `'$0.00'`. 3 | 4 | ```typescript 5 | formatPrice(3.145); // '$3.15' 6 | formatPrice(200); // '$200.00' 7 | formatPrice(); // '$0.00' 8 | formatPrice(null); // '$0.00' 9 | ``` 10 | -------------------------------------------------------------------------------- /modules/40-generics/50-async-functions/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | const asyncMap = async ( 3 | arr: Promise[], 4 | fn: (item: T, index: number) => P, 5 | ) => { 6 | const promises = arr.map(async (item, index) => { 7 | const result = await item; 8 | return fn(result, index); 9 | }); 10 | 11 | return Promise.all(promises); 12 | }; 13 | // END 14 | 15 | export default asyncMap; 16 | -------------------------------------------------------------------------------- /modules/50-objects/20-index-signatures/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Index Signature 3 | tips: 4 | - > 5 | [The chapter on Index Signature in the book TypeScript Deep 6 | Dive](https://basarat.gitbook.io/typescript/type-system/index-signatures) 7 | - > 8 | [Official documentation on Template Literal 9 | Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html) 10 | -------------------------------------------------------------------------------- /modules/10-basics/70-type-aliases/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `getOlderUser()`, которая принимает на вход двух пользователей и возвращает того, который старше. Если пользователи являются ровесниками, то возвращается `null`: 3 | 4 | ```typescript 5 | const user1 = { name: 'Petr', age: 8 }; 6 | ``` 7 | 8 | Определите для пользователя псевдоним, чтобы не дублировать определение его типа в параметрах функции. 9 | -------------------------------------------------------------------------------- /modules/20-functions/40-unknown/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Write the `isPlainObject()` function that checks if the passed value is an object. This function considers that the array is not an object: 3 | 4 | ```typescript 5 | isPlainObject(1); // false 6 | isPlainObject('hexlet'); // false 7 | isPlainObject({}); // true 8 | isPlainObject({ name: 'code-basics' }); // true 9 | isPlainObject([1, 8]); // false 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/20-functions/40-unknown/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `isPlainObject()`, которая проверяет, является ли переданное значение объектом. Эта функция считает, что массив не объект: 3 | 4 | ```typescript 5 | isPlainObject(1); // false 6 | isPlainObject('hexlet'); // false 7 | isPlainObject({}); // true 8 | isPlainObject({ name: 'code-basics' }); // true 9 | isPlainObject([1, 8]); // false 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/test.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { expect, test, vi } from 'vitest'; 3 | 4 | test('hello world', async () => { 5 | const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); 6 | await import(path.join(import.meta.dirname, 'index')); 7 | const firstArg = consoleLogSpy.mock.calls[0]?.[0]; 8 | expect(firstArg).toBe('Hello, World!'); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/10-basics/80-any/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `getParams()` function that takes a query string as input and returns the parameters as an object: 3 | 4 | ```typescript 5 | getParams('per=10&page=5'); 6 | // { per: '10', page: '5' } 7 | getParams('name=hexlet&count=3&order=asc'); 8 | // { name: 'hexlet', count: '3', order: 'asc' } 9 | ``` 10 | 11 | This task is best accomplished through the `reduce()` method. -------------------------------------------------------------------------------- /modules/50-objects/20-index-signatures/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Динамические ключи (Index Signature) 3 | tips: 4 | - > 5 | [Глава о Index Signature в книге TypeScript Deep 6 | Dive](https://basarat.gitbook.io/typescript/type-system/index-signatures) 7 | - > 8 | [Официальная документация про Template Literal 9 | Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html) 10 | -------------------------------------------------------------------------------- /modules/40-generics/20-generic-types/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Дженерики (Типы) 3 | tips: 4 | - > 5 | [Ограничения 6 | дженериков](https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints) 7 | - > 8 | [Параметры типа - extends (generic 9 | constraints)](https://typescript-definitive-guide.ru/book/chapters/Obobshcheniya_(Generics)/#Parametry_tipa_-_extends_(generic_constraints)) 10 | -------------------------------------------------------------------------------- /.github/workflows/Docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | main: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: hexlet-basics/exercises-action@release 17 | with: 18 | DOCKER_USERNAME: ${{ github.actor }} 19 | DOCKER_PASSWORD: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /modules/10-basics/20-typescript-as-a-second-language/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: О языке 3 | tips: 4 | - | 5 | [Telegram Hexlet: JavaScript/TypeScript](https://t.me/hexletcommunity/12) 6 | - | 7 | [TypeScript бойлерплейт](https://github.com/hexlet-boilerplates/typescript-package) 8 | - | 9 | [Playground с возможностью увидеть результат транспиляции TypeScript в JavaScript](https://www.typescriptlang.org/play) 10 | -------------------------------------------------------------------------------- /modules/22-arrays/40-readonly-arrays/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `reverse()`, которая переворачивает массив. Технически она должна возвращать новый массив, в котором элементы расположены в обратном порядке. Используйте модификатор `readonly` для входящего массива. Не используйте встроенный метод `reverse()`. 3 | 4 | ```typescript 5 | reverse([1, 2, 8]); // [8, 2, 1] 6 | reverse([10, 33, 7, 0]); // [0, 7, 33, 10] 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/10-basics/70-type-aliases/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type User = { 3 | name: string; 4 | age: number; 5 | }; 6 | 7 | function getOlderUser(user1: User, user2: User): User | null { 8 | if (user1.age > user2.age) { 9 | return user1; 10 | } 11 | if (user2.age > user1.age) { 12 | return user2; 13 | } 14 | 15 | return null; 16 | } 17 | // END 18 | 19 | export type { User }; 20 | export default getOlderUser; 21 | -------------------------------------------------------------------------------- /modules/10-basics/80-any/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `getParams()`, которая принимает на вход строку запроса (query string) и возвращает параметры в виде объекта: 3 | 4 | ```typescript 5 | getParams('per=10&page=5'); 6 | // { per: '10', page: '5' } 7 | getParams('name=hexlet&count=3&order=asc'); 8 | // { name: 'hexlet', count: '3', order: 'asc' } 9 | ``` 10 | 11 | Эту задачу лучше всего решать через метод `reduce()`. 12 | -------------------------------------------------------------------------------- /modules/10-basics/90-modules/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте namespace `Company`, в котором экспортируется функция `isEmployeeEmail()`. Функция принимает почту и домен. Если е-мейл пользователя содержит указанный домен, то функция возвращает `true`: 3 | 4 | ```typescript 5 | Company.isEmployeeEmail('tirion@hexlet.io', 'hexlet.io'); 6 | // true 7 | 8 | Company.isEmployeeEmail('user@example.com', 'hexlet.io'); 9 | // false 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/25-types/10-type-as-sets/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import type CustomType from './index'; 3 | 4 | test('CustomType', () => { 5 | const numberValue: CustomType = 1; 6 | expect(numberValue).toBe(1); 7 | 8 | const nullValue: CustomType = null; 9 | expect(nullValue).toBe(null); 10 | 11 | const undefinedValue: CustomType = undefined; 12 | expect(undefinedValue).toBe(undefined); 13 | }); 14 | -------------------------------------------------------------------------------- /modules/10-basics/20-typescript-as-a-second-language/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: TypeScript 3 | tips: 4 | - | 5 | [Telegram Hexlet: JavaScript/TypeScript](https://t.me/hexletcommunity/12) 6 | - | 7 | [TypeScript boilerplate](https://github.com/hexlet-boilerplates/typescript-package) 8 | - | 9 | [Playground with the ability to see the result of TypeScript to JavaScript transpilation](https://www.typescriptlang.org/play) 10 | -------------------------------------------------------------------------------- /modules/10-basics/55-objects/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `isComplete()`, которая принимает на вход курс и определяет, завершен ли он. Завершенным считается курс, в который добавлено четыре или более уроков: 3 | 4 | ```typescript 5 | // Определите тип исходя из структуры объекта 6 | const course = { 7 | name: 'Java', 8 | lessons: ['variables', 'functions', 'conditions'], 9 | }; 10 | isComplete(course); // false 11 | ``` 12 | -------------------------------------------------------------------------------- /modules/30-classes/80-abstract-classes/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Abstract classes 3 | definitions: 4 | - name: Abstract class 5 | description: | 6 | is a class that cannot be created directly. It is intended for inheritance only. To create an abstract class, the `abstract` keyword is used. 7 | tips: 8 | - | 9 | [When to use abstract classes in TypeScript](https://khalilstemmler.com/blogs/typescript/abstract-class) 10 | -------------------------------------------------------------------------------- /modules/10-basics/90-modules/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement namespace `Company` in which the `isEmployeeEmail()` function is exported. The function accepts an email and a domain. If the user's email contains the specified domain, the function returns `true`: 3 | 4 | ```typescript 5 | Company.isEmployeeEmail('tirion@hexlet.io', 'hexlet.io'); 6 | // true 7 | 8 | Company.isEmployeeEmail('user@example.com', 'hexlet.io'); 9 | // false 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/22-arrays/25-multi-dimensional-arrays/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import getField from './index'; 3 | 4 | test('function', () => { 5 | expect(getField(1)).toEqual([[null]]); 6 | expect(getField(2)).toEqual([ 7 | [null, null], 8 | [null, null], 9 | ]); 10 | expect(getField(3)).toEqual([ 11 | [null, null, null], 12 | [null, null, null], 13 | [null, null, null], 14 | ]); 15 | }); 16 | -------------------------------------------------------------------------------- /modules/10-basics/80-any/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | 3 | // BEGIN 4 | function getParams(query: string) { 5 | const parts = query.split('&'); 6 | const init: any = {}; 7 | const result = parts.reduce((acc, part) => { 8 | const [key, value] = part.split('='); 9 | acc[key] = value; 10 | return acc; 11 | }, init); 12 | 13 | return result; 14 | } 15 | // END 16 | 17 | export default getParams; 18 | -------------------------------------------------------------------------------- /modules/20-functions/10-function-as-parameter/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import filter from './index'; 3 | 4 | test('function', () => { 5 | const result = filter([], (n) => n > 3); 6 | expect(result).toEqual([]); 7 | 8 | const result2 = filter([3, 2, 8, 9], (n) => n > 3); 9 | expect(result2).toEqual([8, 9]); 10 | 11 | const result3 = filter([3, 2, 8, 9], (n) => n < 8); 12 | expect(result3).toEqual([3, 2]); 13 | }); 14 | -------------------------------------------------------------------------------- /modules/25-types/20-union-types/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import lastIndex from './index'; 4 | 5 | test('lastIndex', () => { 6 | const str = 'jestTest'; 7 | expect(lastIndex(str, 'j')).toBe(0); 8 | expect(lastIndex(str, 't')).toBe(7); 9 | expect(lastIndex(str, 'e')).toBe(5); 10 | expect(lastIndex(str, 'p')).toBeNull(); 11 | 12 | expectTypeOf(lastIndex).returns.toExtend(); 13 | }); 14 | -------------------------------------------------------------------------------- /modules/10-basics/55-objects/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `isComplete()` function that takes a course as input and determines if it is complete. A course is considered complete if four or more lessons have been added to it: 3 | 4 | ```typescript 5 | // Determine the type based on the structure of the object 6 | const course = { 7 | name: 'Java', 8 | lessons: ['variables', 'functions', 'conditions'], 9 | }; 10 | isComplete(course); // false 11 | ``` 12 | -------------------------------------------------------------------------------- /modules/30-classes/10-class-fields/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type CustomFileOptions = { 3 | name: string; 4 | size: number; 5 | }; 6 | 7 | class CustomFile { 8 | name: string; 9 | 10 | size: number; 11 | 12 | constructor({ name, size }: CustomFileOptions) { 13 | this.name = name; 14 | this.size = size; 15 | } 16 | 17 | toString() { 18 | return `${this.name} (${this.size} bytes)`; 19 | } 20 | } 21 | // END 22 | 23 | export default CustomFile; 24 | -------------------------------------------------------------------------------- /modules/22-arrays/20-type-annotations/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import unique from './index'; 3 | 4 | test('function', () => { 5 | expect(unique([])).toEqual([]); 6 | expect(unique([2, 3, -100, -100, -100])).toEqual([2, 3, -100]); 7 | expect(unique(['as', 'good', 'as', 'it', 'gets'])).toEqual([ 8 | 'as', 9 | 'good', 10 | 'it', 11 | 'gets', 12 | ]); 13 | expect(unique([1, 1, 3, 'oops!'])).toEqual([1, 3, 'oops!']); 14 | }); 15 | -------------------------------------------------------------------------------- /modules/22-arrays/25-multi-dimensional-arrays/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `getField()` function that generates a game field for tic-tac-toe. The function takes the field dimension as input and returns an array of arrays of the required size filled with `null` values. 3 | 4 | ```typescript 5 | const field1 = getField(1); 6 | console.log(field1); 7 | // [[null]] 8 | 9 | const field2 = getField(2); 10 | console.log(field2); 11 | // [[null, null], [null, null]] 12 | ``` 13 | -------------------------------------------------------------------------------- /modules/22-arrays/25-multi-dimensional-arrays/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `getField()`, которая генерирует игровое поле для крестиков ноликов. Функция принимает на вход размерность поля и возвращает массив массивов нужного размера, заполненный значениями `null`. 3 | 4 | ```typescript 5 | const field1 = getField(1); 6 | console.log(field1); 7 | // [[null]] 8 | 9 | const field2 = getField(2); 10 | console.log(field2); 11 | // [[null, null], [null, null]] 12 | ``` 13 | -------------------------------------------------------------------------------- /modules/25-types/22-nullable/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Null и Undefined 3 | tips: 4 | - > 5 | [Type Guards и Differentiating 6 | Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types) 7 | - | 8 | [Type Guard](https://basarat.gitbook.io/typescript/type-system/typeguard) 9 | - > 10 | [Optional 11 | chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) 12 | -------------------------------------------------------------------------------- /modules/25-types/22-nullable/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Null и Undefined 3 | tips: 4 | - > 5 | [Type Guards и Differentiating 6 | Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types) 7 | - | 8 | [Type Guard](https://basarat.gitbook.io/typescript/type-system/typeguard) 9 | - > 10 | [Optional 11 | chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) 12 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Implement `Admin` type, which is the intersection of `AdminPermission` and `User` types. Implement the `addAdmin()` function that takes a value with the `User` type and returns a value with the `Admin` type. The value for the `permission` property should be `Permission.READ`. 2 | 3 | ```typescript 4 | const user: User = { login: 'login1' }; 5 | const admin = addAdmin(user); // { login: 'login1', permission: Permission.READ } 6 | ``` 7 | -------------------------------------------------------------------------------- /modules/30-classes/10-class-fields/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте класс `CustomFile`, в конструктор которого передается объект с полями: `name` — именем файла, и `size` — размером в байтах. Внутри класса определите метод `toString()`, который должен вернуть форматированную строку в формате ` ( bytes)`. 3 | 4 | ```typescript 5 | const file = new CustomFile({ name: 'open-world.jpeg', size: 1000 }); 6 | console.log(file.toString()); // open-world.jpeg (1000 bytes) 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/35-interfaces/10-interfaces-overview/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import Car from './index'; 3 | 4 | test('Car', () => { 5 | const porche = new Car(4, 'red', true, 20); 6 | expect(porche.calcFuelNeeded(100)).toBe(20); 7 | 8 | const schoolBus = new Car(30, 'yellow', true, 24); 9 | expect(schoolBus.calcFuelNeeded(25)).toBe(6); 10 | 11 | const lada = new Car(4, 'white', true, 13); 12 | expect(lada.calcFuelNeeded(200)).toBe(26); 13 | }); 14 | -------------------------------------------------------------------------------- /modules/20-functions/85-function-overloads/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | function newYearCongratulate(name: string): string; 3 | function newYearCongratulate(year: number, name: string): string; 4 | function newYearCongratulate(data1: string | number, data2?: string): string { 5 | if (typeof data1 === 'number') { 6 | return `Hi ${data2}! Happy New Year ${data1}!`; 7 | } 8 | 9 | return `Hi ${data1}! Happy New Year!`; 10 | } 11 | // END 12 | 13 | export default newYearCongratulate; 14 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `CustomFile` class, which constructor is passed a file name and size in bytes. Inside the class, define the `toString()` method, which should return a formatted string in the format ` ( bytes)`. Use the parameter properties to populate the class properties. 3 | 4 | ```typescript 5 | const file = new CustomFile('open-world.jpeg', 1000); 6 | console.log(file); // open-world.jpeg (1000 bytes) 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/40-generics/60-generic-classes/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте класс очереди (`Queue`) с методами `enqueue` и `dequeue`. Метод `enqueue` добавляет элемент в конец очереди, а метод `dequeue` удаляет элемент из начала очереди. Если очередь пуста, то при вызове метода `dequeue` должно быть выброшено исключение `Error`: 3 | 4 | ```typescript 5 | const queue = new Queue(); 6 | queue.enqueue(1); 7 | queue.dequeue(); // 1 8 | queue.dequeue(); // Error: Queue is empty 9 | ``` 10 | -------------------------------------------------------------------------------- /modules/20-functions/90-type-narrowing/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `last()` function that extracts the last element from the passed value. The value can be a string or a number. The function returns a value of the same type that was passed as a parameter: 3 | 4 | ```typescript 5 | // Pass a string as a parameter 6 | // The function returns a string 7 | last('hexlet'); // t 8 | 9 | // Pass a number as a parameter 10 | // The function returns a number 11 | last(12345); // 5 12 | ``` 13 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Реализуйте тип `Admin`, который является пересечением типов `AdminPermission` и `User`. Реализуйте функцию `addAdmin()`, которая принимает значение с типом `User` и возвращает значение с типом `Admin`. В качестве значения для свойства `permission` должно быть значение `Permission.READ`. 2 | 3 | ```typescript 4 | const user: User = { login: 'login1' }; 5 | const admin = addAdmin(user); // { login: 'login1', permission: Permission.READ } 6 | ``` 7 | -------------------------------------------------------------------------------- /modules/30-classes/15-class-as-types/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import CustomFile from './index'; 3 | 4 | test('CustomFile', () => { 5 | const file = new CustomFile({ name: 'foo.txt', size: 4000 }); 6 | 7 | expect(file.name).toBe('foo.txt'); 8 | expect(file.size).toBe(4000); 9 | expect(file.toString()).toBe('foo.txt (4000 bytes)'); 10 | 11 | const file2 = new CustomFile(file); 12 | expect(file2.toString()).toBe('(copy) foo.txt (4000 bytes)'); 13 | }); 14 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте класс `CustomFile`, в конструктор которого передается имя файла и размер в байтах. Внутри класса определите метод `toString()`, который должен вернуть форматированную строку в формате ` ( bytes)`. Используйте свойства параметров для заполнения свойств класса. 3 | 4 | ```typescript 5 | const file = new CustomFile('open-world.jpeg', 1000); 6 | console.log(file.toString()); // open-world.jpeg (1000 bytes) 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/30-classes/10-class-fields/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `CustomFile` class, which constructor is passed an object with the fields: `name` - the name of the file, and `size` - the size in bytes. Inside the class, define the `toString()` method, which should return a formatted string in the format ` ( bytes)`. 3 | 4 | ```typescript 5 | const file = new CustomFile({ name: 'open-world.jpeg', size: 1000 }); 6 | console.log(file.toString()); // open-world.jpeg (1000 bytes) 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/30-classes/80-abstract-classes/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Абстрактные классы 3 | definitions: 4 | - name: Абстрактный класс 5 | description: > 6 | это класс, который не может быть создан напрямую. Он предназначен только 7 | для наследования. Для создания абстрактного класса используется ключевое 8 | слово `abstract`. 9 | tips: 10 | - > 11 | [Когда использовать абстрактные классы в 12 | TypeScript](https://khalilstemmler.com/blogs/typescript/abstract-class) 13 | -------------------------------------------------------------------------------- /modules/20-functions/90-type-narrowing/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `last()`, которая извлекает последний элемент из переданного значения. Значением может быть строка или число. Функция возвращает значение того же типа, которое было передано в качестве параметра: 3 | 4 | ```typescript 5 | // Передаем в качестве параметра строку 6 | // Функция возвращает строку 7 | last('hexlet'); // t 8 | 9 | // Передаем в качестве параметра число 10 | // Функция возвращает число 11 | last(12345); // 5 12 | ``` 13 | -------------------------------------------------------------------------------- /modules/22-arrays/50-tuples/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import isTheSamePoint, { type Point } from './index'; 3 | 4 | test('function', () => { 5 | const p1: Point = [1, 2, 3]; 6 | const p2: Point = [1, 2, 3]; 7 | const p3: Point = [0, 2, 3]; 8 | const p4: Point = [1, 21, 3]; 9 | const p5: Point = [1, 2, 13]; 10 | 11 | expect(isTheSamePoint(p1, p2)).toBe(true); 12 | expect(isTheSamePoint(p1, p3)).toBe(false); 13 | expect(isTheSamePoint(p4, p5)).toBe(false); 14 | }); 15 | -------------------------------------------------------------------------------- /modules/25-types/30-intersection-types/index.ts: -------------------------------------------------------------------------------- 1 | enum Permission { 2 | READ, 3 | WRITE, 4 | DELETE, 5 | } 6 | 7 | type User = { 8 | login: string; 9 | }; 10 | 11 | type AdminPermission = { 12 | permission: Permission; 13 | }; 14 | 15 | // BEGIN 16 | type Admin = User & AdminPermission; 17 | 18 | const addAdmin = (user: User): Admin => ({ 19 | ...user, 20 | permission: Permission.READ, 21 | }); 22 | // END 23 | 24 | export { type User, type Admin, Permission }; 25 | export default addAdmin; 26 | -------------------------------------------------------------------------------- /modules/50-objects/30-mapped-types/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `sanitize()` function that takes an object and an array of keys as input. It should also return a new object, but without the specified fields. 3 | 4 | ```typescript 5 | const user = sanitize({ 6 | name: 'John', 7 | password: '1q2w3e', 8 | token: 'test', 9 | }, ['password', 'token']); // { name: string } 10 | 11 | console.log(user); // => { name: 'John' } 12 | ``` 13 | 14 | Note that the output type should also not have these fields. 15 | -------------------------------------------------------------------------------- /modules/25-types/70-variability/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Covariance and contravariance 3 | tips: 4 | - | 5 | [Covariance and contravariance](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)) 6 | definitions: 7 | - name: Covariance 8 | description: | 9 | type property when the types composing the union are subsets of each other. 10 | - name: Contravariance 11 | description: | 12 | type property, when the types composing a union are supersets of each other. 13 | -------------------------------------------------------------------------------- /modules/40-generics/60-generic-classes/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a queue class (`Queue`) with `enqueue` and `dequeue` methods. The `enqueue` method adds an item to the end of the queue, and the `dequeue` method removes an item from the beginning of the queue. If the queue is empty, an `Error` exception must be thrown when the `dequeue` method is called: 3 | 4 | ```typescript 5 | const queue = new Queue(); 6 | queue.enqueue(1); 7 | queue.dequeue(); // 1 8 | queue.dequeue(); // Error: Queue is empty 9 | ``` 10 | -------------------------------------------------------------------------------- /modules/50-objects/30-mapped-types/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `sanitize()`, которая принимает на вход объект и массив ключей. Также она должна возвращать новый объект, но уже без указанных полей. 3 | 4 | ```typescript 5 | const user = sanitize({ 6 | name: 'John', 7 | password: '1q2w3e', 8 | token: 'test', 9 | }, ['password', 'token']); // { name: string } 10 | 11 | console.log(user); // => { name: 'John' } 12 | ``` 13 | 14 | Обратите внимание, что в выходном типе также не должно быть этих полей. 15 | -------------------------------------------------------------------------------- /modules/20-functions/10-function-as-parameter/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a `filter()` function that takes an array of numbers and a predicate as input. The latter will be used to check each number for compliance: 3 | 4 | ```typescript 5 | const numbers = [1, -5, 2, 3, 4, 133]; 6 | filter(numbers, (n) => n > 3); // [4, 133] 7 | filter(numbers, (n) => n % 2 == 0); // [2, 4] 8 | ``` 9 | 10 | Function parameters: 11 | 12 | 1. Array of numbers 13 | 2. Anonymous function that takes a number as input and returns a logical value -------------------------------------------------------------------------------- /modules/25-types/40-assignability/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import form from './index'; 4 | 5 | test('form', () => { 6 | const nameValidator = form.name.validator; 7 | const ageValidator = form.age.validator; 8 | 9 | expect(nameValidator(form.name.value)).toBe(true); 10 | expect(ageValidator(form.age.value)).toBe(false); 11 | 12 | expectTypeOf(nameValidator).parameters.toExtend<[string]>(); 13 | expectTypeOf(ageValidator).parameters.toExtend<[number]>(); 14 | }); 15 | -------------------------------------------------------------------------------- /modules/40-generics/20-generic-types/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a description of the generalized type `MySet`, which is an analog of the JavaScript set `Set`. An example of using an object of this type: 3 | 4 | ```typescript 5 | const s: MySet = ...; 6 | // Adding an item returns the number of items 7 | s.add(1); // 1 8 | s.add(10); // 2 9 | 10 | s.has(1); // true 11 | s.has(8); // false 12 | ``` 13 | 14 | The type includes two methods: `add()` and `has()`. The data inside must be stored in the `items` property. -------------------------------------------------------------------------------- /modules/40-generics/20-generic-types/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте описание обобщенного типа `MySet`, который представляет собой аналог множества `Set` из JavaScript. Пример использования объекта этого типа: 3 | 4 | ```typescript 5 | const s: MySet = ...; 6 | // Добавление возвращает количество элементов 7 | s.add(1); // 1 8 | s.add(10); // 2 9 | 10 | s.has(1); // true 11 | s.has(8); // false 12 | ``` 13 | 14 | Тип включает в себя два метода: `add()` и `has()`. Данные внутри должны храниться в свойстве `items`. 15 | -------------------------------------------------------------------------------- /modules/40-generics/10-generics-overview/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Introductions into generics 3 | definitions: 4 | - name: Generic type 5 | description: | 6 | an abstract type that can be replaced by a concrete type. When we write code, we can describe the behavior of generalized types or their relationship with other generalized types without knowing which type will be used in the place where they are used. 7 | tips: 8 | - | 9 | [Parametric polymorphism](https://en.wikipedia.org/wiki/Parametric_polymorphism) 10 | -------------------------------------------------------------------------------- /modules/35-interfaces/10-interfaces-overview/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Вам дан интерфейс IVehicle. Задача состоит в том, чтобы на основе этого интерфейса реализовать класс Car, который будет иметь метод calcFuelNeeded, принимающий расстояние в километрах и возвращающий расход топлива на указанную дистанцию. Также у класса Car должна быть функция-конструктор, которая принимает и реализует свойства, указанные в интерфейсе. 3 | 4 | ```typescript 5 | const porche = new Car(4, 'red', true, 20); 6 | console.log(porche.calcFuelNeeded(200)); // 40 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/50-objects/15-object/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import extract from './index'; 3 | 4 | test('extract', () => { 5 | const obj = { 6 | name: 'John', 7 | age: 30, 8 | address: { 9 | street: 'Main Street', 10 | number: 123, 11 | }, 12 | }; 13 | 14 | expect(extract(obj, ['name', 'address'])).toEqual({ 15 | name: 'John', 16 | address: obj.address, 17 | }); 18 | 19 | expect(extract(obj, ['name', 'lastName'])).toEqual({ 20 | name: 'John', 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /modules/25-types/40-assignability/index.ts: -------------------------------------------------------------------------------- 1 | type Form = { 2 | age: { 3 | value: number; 4 | validator: (val: number) => boolean; 5 | }; 6 | name: { 7 | value: string; 8 | validator: (val: string) => boolean; 9 | }; 10 | }; 11 | 12 | const form: Form = { 13 | // BEGIN 14 | name: { 15 | value: 'Kirill', 16 | validator: (val: string) => val.length > 1, 17 | }, 18 | age: { 19 | value: 17, 20 | validator: (val: number) => val > 18, 21 | }, 22 | // END 23 | }; 24 | 25 | export default form; 26 | -------------------------------------------------------------------------------- /modules/10-basics/40-named-functions/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import getHiddenCard from './index'; 4 | 5 | test('getHiddenCard', () => { 6 | expect(getHiddenCard('1234123412341234')).toEqual('****1234'); 7 | expect(getHiddenCard('1234123412344321')).toEqual('****4321'); 8 | expect(getHiddenCard('1234123412344321', 2)).toEqual('**4321'); 9 | expect(getHiddenCard('1234123412341234', 12)).toEqual('************1234'); 10 | 11 | expectTypeOf(getHiddenCard).returns.toExtend(); 12 | }); 13 | -------------------------------------------------------------------------------- /modules/20-functions/10-function-as-parameter/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `filter()`, которая принимает на вход массив чисел и предикат. Последний будет использоваться для проверки каждого числа на соответствие требованиям: 3 | 4 | ```typescript 5 | const numbers = [1, -5, 2, 3, 4, 133]; 6 | filter(numbers, (n) => n > 3); // [4, 133] 7 | filter(numbers, (n) => n % 2 == 0); // [2, 4] 8 | ``` 9 | 10 | Параметры функции: 11 | 12 | 1. Массив чисел 13 | 2. Анонимная функция, которая принимает на вход число и возвращает логическое значение 14 | -------------------------------------------------------------------------------- /modules/35-interfaces/10-interfaces-overview/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | You are given the IVehicle interface. The task is to implement a Car class based on this interface, which will have a calcFuelNeeded method that accepts the distance in kilometers and returns the fuel consumption for the specified distance. The Car class should also have a constructor function that accepts and implements the properties specified in the interface. 3 | 4 | ```typescript 5 | const porche = new Car(4, 'red', true, 20); 6 | console.log(porche.calcFuelNeeded(200)); // 40 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/50-objects/35-mapping-modifiers/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type DeepReadonly = { 3 | readonly [P in keyof T]: T[P] extends object ? DeepReadonly : T[P]; 4 | }; 5 | 6 | const deepFreeze = (obj: T): DeepReadonly => { 7 | const freezedObj = Object.freeze(obj); 8 | 9 | Object.values(freezedObj).forEach((value) => { 10 | if (typeof value === 'object' && value !== null) { 11 | deepFreeze(value); 12 | } 13 | }); 14 | 15 | return freezedObj; 16 | }; 17 | // END 18 | 19 | export default deepFreeze; 20 | -------------------------------------------------------------------------------- /modules/20-functions/30-void/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import forEach from './index'; 3 | 4 | test('forEach', () => { 5 | const result: number[] = []; 6 | forEach([], (n: number) => result.push(n)); 7 | expect(result).toEqual([]); 8 | 9 | const result2: number[] = []; 10 | forEach([3, 9], (n: number) => result2.push(n + 1)); 11 | expect(result2).toEqual([4, 10]); 12 | 13 | const result3: number[] = []; 14 | forEach([8, 9], (n: number, i) => result3.push(n + i)); 15 | expect(result3).toEqual([8, 10]); 16 | }); 17 | -------------------------------------------------------------------------------- /modules/22-arrays/20-type-annotations/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `unique()`, которая убирает дубликаты из массива. Функция принимает на вход массив чисел и строк. Результатом работы функции должен быть новый массив, в котором сохраняется только первое вхождение каждого элемента. Порядок значений результата определяется порядком их появления в массиве. 3 | 4 | ```typescript 5 | unique([9, 9, 3, 8, 8]); // [9, 3, 8] 6 | unique(['twinkle', 'twinkle', 'little', 'bat']); // ['twinkle', 'little', 'bat'] 7 | unique([1, 1, 3, 'oops!']); // [1, 3, 'oops!'] 8 | ``` 9 | -------------------------------------------------------------------------------- /modules/22-arrays/50-tuples/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Create and export a `Point` type that describes a point in space consisting of three coordinates: x, y, z. 3 | 4 | Implement the `isTheSamePoint()` function that checks two points to see if they are the same. Two points are the same if all their coordinates match: 5 | 6 | ```typescript 7 | const p1: Point = [1, 3, 4]; 8 | const p2: Point = [1, 3, 4]; 9 | const p3: Point = [0, 8, 4]; 10 | 11 | isTheSamePoint(p1, p2); // true 12 | isTheSamePoint(p1, p3); // false 13 | isTheSamePoint(p2, p3); // false 14 | ``` 15 | -------------------------------------------------------------------------------- /modules/10-basics/description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Основы TypeScript 3 | description: | 4 | TypeScript – один из самых популярных языков программирования в мире. Он используется для создания интерактивных веб-страниц, мобильных приложений, в серверной разработке. 5 | Изучать TS мы будем с нуля, с самых азов. Первый модуль – плацдарм для написания осмысленных программ. В нем мы разберем, как написать свой первый код на TS. Расскажем, что такое комментарии и зачем они нужны. На примере проверки ваших решений рассмотрим, что такое тестирование и как читать вывод тестов. 6 | -------------------------------------------------------------------------------- /modules/22-arrays/50-tuples/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Создайте и экспортируйте тип `Point`, который описывает точку в пространстве, состоящую из трех координат: x, y, z. 3 | 4 | Реализуйте функцию `isTheSamePoint()`, которая проверяет две точки на их одинаковое расположение. Две точки совпадают, если совпадают все их координаты: 5 | 6 | ```typescript 7 | const p1: Point = [1, 3, 4]; 8 | const p2: Point = [1, 3, 4]; 9 | const p3: Point = [0, 8, 4]; 10 | 11 | isTheSamePoint(p1, p2); // true 12 | isTheSamePoint(p1, p3); // false 13 | isTheSamePoint(p2, p3); // false 14 | ``` 15 | -------------------------------------------------------------------------------- /modules/25-types/25-literal-types/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `makeTurn()` function that takes a `left` or `right` string and moves a turtle back and forth across a one-dimensional array of fixed size with five elements. If the turtle moves outside the array, an exception is thrown. 3 | 4 | ```typescript 5 | const { makeTurn, state } = startGame(); 6 | console.log(state); // ['turtle', null, null, null, null] 7 | 8 | makeTurn('left') // ERROR 9 | 10 | makeTurn('right'); 11 | makeTurn('right'); 12 | console.log(state); // [null, null, 'turtle', null, null] 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/40-generics/50-async-functions/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте асинхронный вариант функции `map()` - `asyncMap()`. Первым аргументом `asyncMap()` принимает массив с Promise. Вторым — функцию, которая применяется к каждому элементу. Функция должна вернуть массив с результатами выполнения функции для каждого элемента: 3 | 4 | ```typescript 5 | const promisedNumbers = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; 6 | 7 | asyncMap(promisedNumbers, (num, index) => num * index).then((result) => { 8 | console.log(result); // [0, 2, 6] 9 | }); 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/10-basics/90-modules/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-namespace */ 2 | // BEGIN 3 | namespace Company { 4 | export function isEmployeeEmail(email: string, domain: string): boolean { 5 | return email.endsWith(`@${domain}`); 6 | } 7 | } 8 | // END 9 | 10 | type User = { 11 | email: string; 12 | }; 13 | 14 | function authorize(user: User | null): boolean { 15 | const companyDomain = 'hexlet.io'; 16 | 17 | const email = user?.email ?? ''; 18 | 19 | return Company.isEmployeeEmail(email, companyDomain); 20 | } 21 | 22 | export default authorize; 23 | -------------------------------------------------------------------------------- /modules/20-functions/20-optional-parameters-in-callbacks/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import map from './index'; 3 | 4 | test('map', () => { 5 | const result = map([], (n: number) => n + 3); 6 | expect(result).toEqual([]); 7 | 8 | const result2 = map([3, 9], (n: number) => n - 3); 9 | expect(result2).toEqual([0, 6]); 10 | 11 | const result3 = map([8, 9], (n: number) => n + 8); 12 | expect(result3).toEqual([16, 17]); 13 | 14 | const result4 = map([10, 10, 10], (n: number, index) => n + index); 15 | expect(result4).toEqual([10, 11, 12]); 16 | }); 17 | -------------------------------------------------------------------------------- /modules/25-types/25-literal-types/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `makeTurn()`, которая принимает строку `left` или `right` и перемещает черепашку вперед-назад по одномерному массиву фиксированного размера с пятью элементами. Если черепашка выходит за пределы массива, то выбрасывается исключение. 3 | 4 | ```typescript 5 | const { makeTurn, state } = startGame(); 6 | console.log(state); // ['turtle', null, null, null, null] 7 | 8 | makeTurn('left') // ERROR 9 | 10 | makeTurn('right'); 11 | makeTurn('right'); 12 | console.log(state); // [null, null, 'turtle', null, null] 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/30-classes/70-static-property/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Another useful use of static properties and methods is to create factory methods. A factory method is a static method that returns a new instance of a class. Implement a `UserResponse` class with a `user: string` field and a factory method `fromArray` that takes an array and returns an array of instances of the `UserResponse` class: 3 | 4 | ```typescript 5 | const response = UserResponse.fromArray(['user1', 'user2', 'user3']); 6 | console.log(response[0].user); // user1 7 | console.log(response[0] instanceof UserResponse); // true 8 | ``` 9 | -------------------------------------------------------------------------------- /modules/10-basics/90-modules/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Modules 3 | tips: 4 | - | 5 | [Official documentation about modules](https://www.typescriptlang.org/docs/handbook/modules.html) 6 | - | 7 | [How module resolution happens](https://www.typescriptlang.org/docs/handbook/module-resolution.html#how-nodejs-resolves-modules) 8 | - | 9 | [Official documentation about namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html) 10 | - | 11 | [Method includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) 12 | -------------------------------------------------------------------------------- /modules/22-arrays/20-type-annotations/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `unique()` function that removes duplicates from an array. The function accepts an array of numbers and strings as input. The result of the function should be a new array in which only the first occurrence of each element is stored. The order of the result values is determined by the order of their occurrence in the array. 3 | 4 | ```typescript 5 | unique([9, 9, 3, 8, 8]); // [9, 3, 8] 6 | unique(['twinkle', 'twinkle', 'little', 'bat']); // ['twinkle', 'little', 'bat'] 7 | unique([1, 1, 3, 'oops!']); // [1, 3, 'oops!'] 8 | ``` 9 | -------------------------------------------------------------------------------- /modules/30-classes/70-static-property/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Другое полезное применение статических свойств и методов — создание фабричных методов. Фабричный метод — это статический метод, который возвращает новый экземпляр класса. Реализуйте класс `UserResponse` с полем `user: string` и фабричный метод `fromArray`, который принимает массив и возвращает массив экземпляров класса `UserResponse`: 3 | 4 | ```typescript 5 | const response = UserResponse.fromArray(['user1', 'user2', 'user3']); 6 | console.log(response[0].user); // user1 7 | console.log(response[0] instanceof UserResponse); // true 8 | ``` 9 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import filterAnagrams from './index'; 4 | 5 | test('function', () => { 6 | expect(filterAnagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada'])).toEqual([ 7 | 'aabb', 8 | 'bbaa', 9 | ]); 10 | 11 | expect( 12 | filterAnagrams('racer', ['crazer', 'carer', 'racar', 'caers', 'racer']), 13 | ).toEqual(['carer', 'racer']); 14 | 15 | expect(filterAnagrams('laser', ['lazing', 'lazy', 'lacer'])).toEqual([]); 16 | 17 | expectTypeOf(filterAnagrams).returns.toExtend(); 18 | }); 19 | -------------------------------------------------------------------------------- /modules/10-basics/60-enums/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import buildModal, { ModalStatus } from './index'; 4 | 5 | test('function', () => { 6 | expect(buildModal('hexlet', ModalStatus.Opened)).toEqual({ 7 | text: 'hexlet', 8 | status: ModalStatus.Opened, 9 | }); 10 | 11 | expect(buildModal('code-basics', ModalStatus.Closed)).toEqual({ 12 | text: 'code-basics', 13 | status: ModalStatus.Closed, 14 | }); 15 | 16 | expectTypeOf(buildModal).returns.toExtend<{ 17 | text: string; 18 | status: ModalStatus; 19 | }>(); 20 | }); 21 | -------------------------------------------------------------------------------- /modules/40-generics/50-async-functions/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement an asynchronous variant of the `map()` function - `asyncMap()`. The first argument of `asyncMap()` takes an array with Promise. The second is a function that is applied to each element. The function should return an array with the results of the function execution for each element: 3 | 4 | ```typescript 5 | const promisedNumbers = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; 6 | 7 | asyncMap(promisedNumbers, (num, index) => num * index).then((result) => { 8 | console.log(result); // [0, 2, 6] 9 | }); 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/10-basics/description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: TypeScript Fundamentals 3 | description: | 4 | TypeScript is one of the most popular programming languages in the world. It is used to create interactive web pages, mobile applications, and in server-side development. 5 | We will learn TS from scratch, from the very basics. The first module is a springboard for writing meaningful programs. In it, we will learn how to write your first code in TS. We will tell you what comments are and why you need them. On the example of checking your decisions, we will consider what testing is and how to read test output. 6 | -------------------------------------------------------------------------------- /modules/30-classes/20-members-visibility/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `ImageCustomFile` class, which extends (`extends`) the `CustomFile` class with additional private fields: `width`, `height`. Also override the `toString()` method. Now it should additionally output `x`. 3 | 4 | ```typescript 5 | const imageCustomFile = new ImageCustomFile({ 6 | name: 'image.png', 7 | size: 100, 8 | width: 200, 9 | height: 300, 10 | }); 11 | console.log(imageCustomFile.toString()); // image.png (100 bytes) 200x300 12 | ``` 13 | 14 | To call a method of a parent class, use `super.toString()`. 15 | -------------------------------------------------------------------------------- /modules/35-interfaces/30-interface-implementation/index.ts: -------------------------------------------------------------------------------- 1 | type Entry = { 2 | [key: string]: number; 3 | }; 4 | 5 | interface IPhonebook { 6 | get(key: string): number | null; 7 | set(key: string, value: number): void; 8 | } 9 | 10 | // BEGIN 11 | class Phonebook implements IPhonebook { 12 | private readonly entries: Entry = {}; 13 | 14 | get(key: string): number | null { 15 | return key in this.entries ? this.entries[key] : null; 16 | } 17 | 18 | set(key: string, value: number): void { 19 | this.entries[key] = value; 20 | } 21 | } 22 | // END 23 | 24 | export default Phonebook; 25 | -------------------------------------------------------------------------------- /modules/50-objects/20-index-signatures/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте интерфейс `EmployeeSalary`, где ключом выступает имя (`string`), а значением — зарплата (`number`). Также реализуйте функцию `buildSalaryStatistics(employees: EmployeeSalary): SalaryStatistics`, которая должна возвращать минимальную (поле `min`), среднюю (поле `avg`) и самую высокую (поле `max`) зарплату. 3 | 4 | ```typescript 5 | const employees: EmployeeSalary = { 6 | mango: 100, 7 | poly: 50, 8 | ajax: 150, 9 | }; 10 | 11 | employees.ironMan = 1000; 12 | 13 | buildSalaryStatistics(employees); // { min: 50, max: 1000, avg: 325 } 14 | ``` 15 | -------------------------------------------------------------------------------- /modules/50-objects/20-index-signatures/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `EmployeeSalary` interface, where the key is the name (`string`) and the value is the salary (`number`). Also implement the function `buildSalaryStatistics(employees: EmployeeSalary): SalaryStatistics`, which should return the minimum (`min` field), average (`avg` field) and highest (`max` field) salary. 3 | 4 | ```typescript 5 | const employees: EmployeeSalary = { 6 | mango: 100, 7 | poly: 50, 8 | ajax: 150, 9 | }; 10 | 11 | employees.ironMan = 1000; 12 | 13 | buildSalaryStatistics(employees); // { min: 50, max: 1000, avg: 325 } 14 | ``` 15 | -------------------------------------------------------------------------------- /modules/30-classes/20-members-visibility/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте класс `ImageCustomFile`, который расширяет (`extends`) класс `CustomFile` дополнительными приватными полями: `width`, `height`. Также переопределите метод `toString()`. Теперь он должен дополнительно выводить `x`. 3 | 4 | ```typescript 5 | const imageCustomFile = new ImageCustomFile({ 6 | name: 'image.png', 7 | size: 100, 8 | width: 200, 9 | height: 300, 10 | }); 11 | console.log(imageCustomFile.toString()); // image.png (100 bytes) 200x300 12 | ``` 13 | 14 | Чтобы вызвать метод родительского класса, используйте `super.toString()`. 15 | -------------------------------------------------------------------------------- /modules/20-functions/30-void/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Попробуйте самостоятельно определить функцию `forEach()` для чисел: 3 | 4 | ```typescript 5 | forEach([1, 2, 3], (n) => console.log(n)); 6 | // 1 7 | // 2 8 | // 3 9 | 10 | const result = []; 11 | forEach([1, 2, 3], (n) => result.push(n)); 12 | // [1, 2, 3] 13 | ``` 14 | 15 | Параметры функции: 16 | 17 | 1. Массив чисел 18 | 2. Анонимная функция, которая принимает на вход число и возвращает `void`. У этой функции есть необязательный параметр — индекс элемента в массиве 19 | 20 | ```typescript 21 | forEach([8, 9], (n, index) => console.log(index + n)); 22 | // 8 23 | // 10 24 | ``` 25 | -------------------------------------------------------------------------------- /modules/20-functions/30-void/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Try defining the `forEach()` function for numbers on your own: 3 | 4 | ```typescript 5 | forEach([1, 2, 3], (n) => console.log(n)); 6 | // 1 7 | // 2 8 | // 3 9 | 10 | const result = []; 11 | forEach([1, 2, 3], (n) => result.push(n)); 12 | // [1, 2, 3] 13 | ``` 14 | 15 | Function parameters: 16 | 17 | 1. Array of numbers 18 | 2. Anonymous function that takes a number as input and returns `void`. This function has an optional parameter - the index of the element in the array 19 | 20 | ```typescript 21 | forEach([8, 9], (n, index) => console.log(index + n)); 22 | // 8 23 | // 10 24 | ``` 25 | -------------------------------------------------------------------------------- /modules/30-classes/80-abstract-classes/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | abstract class Clock { 3 | constructor( 4 | protected hours: number, 5 | protected minutes: number, 6 | protected seconds: number, 7 | ) {} 8 | 9 | tick(): void { 10 | this.seconds += 1; 11 | if (this.seconds === 60) { 12 | this.seconds = 0; 13 | this.minutes += 1; 14 | } 15 | if (this.minutes === 60) { 16 | this.minutes = 0; 17 | this.hours += 1; 18 | } 19 | if (this.hours === 24) { 20 | this.hours = 0; 21 | } 22 | } 23 | 24 | abstract render(): string; 25 | } 26 | // END 27 | 28 | export default Clock; 29 | -------------------------------------------------------------------------------- /modules/35-interfaces/10-interfaces-overview/index.ts: -------------------------------------------------------------------------------- 1 | interface IVehicle { 2 | seats: number; 3 | colour: string; 4 | canHavePassengers: boolean; 5 | fuelPer100Kilometers: number; 6 | calcFuelNeeded(distance: number): number; 7 | } 8 | 9 | // BEGIN 10 | class Car implements IVehicle { 11 | constructor( 12 | public seats: number, 13 | public colour: string, 14 | public canHavePassengers: boolean, 15 | public fuelPer100Kilometers: number, 16 | ) {} 17 | 18 | calcFuelNeeded(distance: number) { 19 | return (this.fuelPer100Kilometers / 100) * distance; 20 | } 21 | } 22 | // END 23 | 24 | export default Car; 25 | -------------------------------------------------------------------------------- /modules/50-objects/20-index-signatures/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import { buildSalaryStatistics, type EmployeeSalary } from '.'; 3 | 4 | test('buildSalaryStatistics', () => { 5 | const employees: EmployeeSalary = { 6 | mango: 100, 7 | poly: 50, 8 | ajax: 150, 9 | }; 10 | 11 | const expected = { 12 | min: 50, 13 | max: 150, 14 | avg: 100, 15 | }; 16 | 17 | expect(buildSalaryStatistics(employees)).toEqual(expected); 18 | 19 | employees.ironMan = 1000; 20 | expect(buildSalaryStatistics(employees)).toEqual({ 21 | min: 50, 22 | max: 1000, 23 | avg: 325, 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /modules/30-classes/70-static-property/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import UserResponse from './index'; 3 | 4 | test('UserResponse', () => { 5 | const users = ['user1', 'user2']; 6 | const result = UserResponse.fromArray(users); 7 | 8 | expect(result).toEqual([ 9 | new UserResponse('user1'), 10 | new UserResponse('user2'), 11 | ]); 12 | expect(result[0]).toBeInstanceOf(UserResponse); 13 | 14 | const users2 = ['user3', 'user4', 'user5']; 15 | const result2 = UserResponse.fromArray(users2); 16 | 17 | expect(result2[0].user).toBe('user3'); 18 | expect(result2[2]).toBeInstanceOf(UserResponse); 19 | }); 20 | -------------------------------------------------------------------------------- /modules/35-interfaces/30-interface-implementation/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Using the provided `IPhonebook` interface and the `Entry` type, implement the `Phonebook` class, which represents a telephone directory with the following properties: 3 | 4 | - `entries` is a database, an object whose entries represent names as keys and phone numbers as values. The property must be immutable and read-only 5 | - `get` - method that returns a phone by name 6 | - `set` - method that writes the name and phone to the directory 7 | 8 | Examples: 9 | ```typescript 10 | const myNote = new Phonebook(); 11 | myNote.set('help', 911); 12 | myNote.get('help'); // 911 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/10-basics/60-enums/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | 1. Реализуйте перечисление `ModalStatus` с двумя значениями: Opened и Closed 3 | 2. Реализуйте функцию `buildModal()`. Он возвращает объект, который описывает модальное окно. Параметры функции: 4 | 5 | * Текст, который должен быть внутри окна после инициализации; 6 | * Статус, с которым надо создать объект окна. 7 | 8 | Функция возвращает объект с двумя полями: `text` (здесь хранится переданный текст) и `status` (здесь хранится переданный статус) 9 | 10 | ```typescript 11 | const modal = buildModal('hexlet forever', ModalStatus.Opened); 12 | // { text: 'hexlet forever', status: ModalStatus.Opened } 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/50-objects/20-index-signatures/index.ts: -------------------------------------------------------------------------------- 1 | interface SalaryStatistics { 2 | min: number; 3 | max: number; 4 | avg: number; 5 | } 6 | 7 | // BEGIN 8 | interface EmployeeSalary { 9 | [name: string]: number; 10 | } 11 | 12 | const buildSalaryStatistics = (employees: EmployeeSalary): SalaryStatistics => { 13 | const salaries = Object.values(employees); 14 | const min = Math.min(...salaries); 15 | const max = Math.max(...salaries); 16 | const avg = 17 | salaries.reduce((acc, salary) => acc + salary, 0) / salaries.length; 18 | 19 | return { min, max, avg }; 20 | }; 21 | // END 22 | 23 | export { type EmployeeSalary, buildSalaryStatistics }; 24 | -------------------------------------------------------------------------------- /modules/10-basics/70-type-aliases/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import getOlderUser, { type User } from './index'; 4 | 5 | test('function', () => { 6 | const user1 = { 7 | name: 'sem', 8 | age: 3, 9 | }; 10 | 11 | const user2 = { 12 | name: 'inna', 13 | age: 5, 14 | }; 15 | 16 | const user3 = { 17 | name: 'mika', 18 | age: 5, 19 | }; 20 | 21 | expect(getOlderUser(user1, user2)).toEqual(user2); 22 | expect(getOlderUser(user2, user1)).toEqual(user2); 23 | 24 | expect(getOlderUser(user2, user3)).toBeNull(); 25 | 26 | expectTypeOf(getOlderUser).returns.toExtend(); 27 | }); 28 | -------------------------------------------------------------------------------- /modules/40-generics/10-generics-overview/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Введение в дженерики 3 | definitions: 4 | - name: Обобщенный тип 5 | description: > 6 | абстрактный тип, на место которого можно подставить конкретный тип. Когда 7 | мы пишем код, мы можем описать поведение обобщенных типов или их связь с 8 | другими обобщенными типами, не зная какой тип будет использован в месте их 9 | использования. 10 | tips: 11 | - > 12 | [Параметрический 13 | полиморфизм](https://ru.wikipedia.org/wiki/Параметрический_полиморфизм) 14 | - > 15 | [Полиморфизм простыми 16 | словами](https://medium.com/devschacht/polymorphism-207d9f9cd78) 17 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Привет, Мир! 3 | tips: 4 | - | 5 | [Repl для экспериментов с TypeScript](https://www.typescriptlang.org/play) 6 | - > 7 | [Инструкция по установке 8 | TypeScript](https://github.com/Hexlet/ru-instructions/blob/main/typescript.md) 9 | - | 10 | [strict в tsconfig](https://www.typescriptlang.org/tsconfig#strict) 11 | - > 12 | [Документация по флагам запуска 13 | tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html#compiler-options) 14 | - > 15 | [Статья "Как устроена система типов в 16 | TypeScript"](https://ru.hexlet.io/blog/posts/sistema-tipov-v-typescript) 17 | -------------------------------------------------------------------------------- /modules/10-basics/60-enums/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | 1. Implement a `ModalStatus` enumeration with two values: Opened and Closed 3 | 2. Implement the `buildModal()` function. It returns an object that describes a modal window. Function parameters: 4 | 5 | * The text that should be inside the window after initialization 6 | * Status with which to create a window object 7 | 8 | The function returns an object with two fields: `text` (here the transmitted text is stored) and `status` (here the transmitted status is stored) 9 | 10 | ```typescript 11 | const modal = buildModal('hexlet forever', ModalStatus.Opened); 12 | // { text: 'hexlet forever', status: ModalStatus.Opened } 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/40-generics/60-generic-classes/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import Queue from './index'; 3 | 4 | test('Queue', () => { 5 | const queue = new Queue(); 6 | queue.enqueue(1); 7 | queue.enqueue(2); 8 | queue.enqueue(3); 9 | expect(queue.dequeue()).toBe(1); 10 | expect(queue.dequeue()).toBe(2); 11 | expect(queue.dequeue()).toBe(3); 12 | expect(() => queue.dequeue()).toThrow(); 13 | 14 | const queue2 = new Queue(); 15 | queue2.enqueue('one'); 16 | queue2.enqueue('two'); 17 | expect(queue2.dequeue()).toBe('one'); 18 | expect(queue2.dequeue()).toBe('two'); 19 | expect(() => queue2.dequeue()).toThrow(); 20 | }); 21 | -------------------------------------------------------------------------------- /modules/25-types/70-variability/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ковариантность и контравариантность 3 | tips: 4 | - > 5 | [Ковариантность и 6 | контравариантность](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)) 7 | - | 8 | [Теория программирования: Вариантность](https://habr.com/ru/post/477448/) 9 | definitions: 10 | - name: Ковариантность 11 | description: > 12 | свойство типов, когда типы, составляющие объединение, являются 13 | подмножествами друг друга. 14 | - name: Контравариантность 15 | description: > 16 | свойство типов, когда типы, составляющие объединение, являются 17 | надмножествами друг друга. 18 | -------------------------------------------------------------------------------- /modules/35-interfaces/30-interface-implementation/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | С помощью предоставленного интерфейса `IPhonebook` и типа `Entry` реализуйте класс `Phonebook`, который представляет телефонный справочник со следующими свойствами: 3 | 4 | - `entries` — база данных, объект, записи в котором представляют собой имена в качестве ключей и телефоны в качестве значений. Свойство должно быть неизменяемым и доступным только для чтения 5 | - `get` — метод, возвращающий телефон по имени 6 | - `set` — метод, записывающий имя и телефон в справочник 7 | 8 | Примеры: 9 | ```typescript 10 | const myNote = new Phonebook(); 11 | myNote.set('help', 911); 12 | myNote.get('help'); // 911 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/25-types/70-variability/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | // BEGIN 3 | type Transaction = { 4 | apply: (amount: number) => number; 5 | }; 6 | 7 | type Wallet = { 8 | transactions: Transaction[]; 9 | balance: number; 10 | }; 11 | 12 | const applyTransactions = (wallet: Wallet) => { 13 | try { 14 | let { balance } = wallet; 15 | 16 | wallet.transactions.forEach((transaction) => { 17 | balance = transaction.apply(balance); 18 | }); 19 | return balance; 20 | } catch (e) { 21 | return wallet.balance; 22 | } 23 | }; 24 | // END 25 | 26 | export type { Transaction, Wallet }; 27 | export default applyTransactions; 28 | -------------------------------------------------------------------------------- /modules/30-classes/30-class-extending/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте класс `HttpError`, который должен наследоваться от встроенного класса `Error` и принимать первым параметром код ошибки, а вторым — `message`. Также реализуйте классы `NotFoundError`, `UnauthorizedError`, `ForbiddenError`. Каждый из них должен наследоваться от класса `HttpError` и иметь свойство `status`, которое равно коду ошибки и `message` — сообщение, передающееся в базовый класс. Коды ошибок: `404`, `401`, `403`. 3 | 4 | ```typescript 5 | import { NotFoundError } from './errors'; 6 | 7 | const error = new NotFoundError('Not Found'); 8 | console.log(error.status); // 404 9 | console.log(error.message); // Not Found 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/30-classes/15-class-as-types/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | type OptionName = string; 3 | type OptionSize = number; 4 | type CustomFileOptions = { name: OptionName; size: OptionSize }; 5 | 6 | class CustomFile { 7 | name: OptionName; 8 | 9 | size: OptionSize; 10 | 11 | private isCopy: boolean; 12 | 13 | constructor(options: CustomFileOptions) { 14 | this.name = options.name; 15 | this.size = options.size; 16 | this.isCopy = options instanceof CustomFile; 17 | } 18 | 19 | toString(): string { 20 | const copyString = this.isCopy ? '(copy) ' : ''; 21 | return `${copyString}${this.name} (${this.size} bytes)`; 22 | } 23 | } 24 | // END 25 | 26 | export default CustomFile; 27 | -------------------------------------------------------------------------------- /modules/35-interfaces/20-interface-using/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | You are given several interfaces. Based on them, create the ISuperman interface. ISuperMan must have a guessWho method that accepts and returns a string. 3 | 4 | Based on the ISuperMan interface, create a `superMan` object. The guessWho method should work as follows: if any value other than superman (in any case) comes as a string in the argument, it should return the guess ‘It's a ${value}?’, otherwise ‘It's a ${value}!’. 5 | 6 | ```typescript 7 | console.log(superMan.guessWho('bird')); // "It's a bird?"; 8 | console.log(superMan.guessWho('plane')); "It's a plane?"; 9 | console.log(superMan.guessWho('superman')); "It's a superman!"; 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/35-interfaces/20-interface-using/index.ts: -------------------------------------------------------------------------------- 1 | interface IFlying { 2 | canFly: true; 3 | } 4 | 5 | interface IBird extends IFlying { 6 | isLiving: true; 7 | } 8 | 9 | interface IPlane extends IFlying { 10 | canCarryPeople: true; 11 | } 12 | 13 | // BEGIN 14 | interface ISuperman extends IBird, IPlane { 15 | guessWho: (guess: string) => string; 16 | } 17 | 18 | const superMan: ISuperman = { 19 | canFly: true, 20 | isLiving: true, 21 | canCarryPeople: true, 22 | guessWho: (guess) => 23 | guess.toLowerCase() !== 'superman' 24 | ? `It's a ${guess}?` 25 | : `It's a ${guess}!`, 26 | }; 27 | // END 28 | 29 | export { superMan, type ISuperman, type IBird, type IPlane }; 30 | -------------------------------------------------------------------------------- /modules/20-functions/20-optional-parameters-in-callbacks/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `map()`. Она должна принимать на вход массив чисел и колбек, который будет использоваться для преобразования каждого числа из массива в другое число. Функция возвращает массив с новыми числами: 3 | 4 | ```typescript 5 | map([3, 9], (n) => n - 3); 6 | // [0, 6] 7 | 8 | map([8, 9], (n) => n + 8); 9 | // [16, 17] 10 | ``` 11 | 12 | Параметры функции: 13 | 14 | 1. Массив чисел 15 | 2. Анонимная функция, которая принимает на вход число и возвращает число. У этой функции есть необязательный параметр — индекс элемента в массиве 16 | 17 | ```typescript 18 | map([8, 9], (n, index) => index + n); 19 | // [8, 10] 20 | ``` 21 | -------------------------------------------------------------------------------- /modules/30-classes/30-class-extending/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | class HttpError extends Error { 3 | constructor( 4 | public status: number, 5 | message: string, 6 | ) { 7 | super(message); 8 | } 9 | } 10 | 11 | class NotFoundError extends HttpError { 12 | constructor(message: string) { 13 | super(404, message); 14 | } 15 | } 16 | 17 | class UnauthorizedError extends HttpError { 18 | constructor(message: string) { 19 | super(401, message); 20 | } 21 | } 22 | 23 | class ForbiddenError extends HttpError { 24 | constructor(message: string) { 25 | super(403, message); 26 | } 27 | } 28 | // END 29 | 30 | export { HttpError, NotFoundError, UnauthorizedError, ForbiddenError }; 31 | -------------------------------------------------------------------------------- /modules/35-interfaces/20-interface-using/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Вам даны несколько интерфейсов. На их основе создайте интерфейс ISuperman. ISuperMan должен иметь метод guessWho, принимающий и возвращающий строку. 3 | 4 | На основе интерфейса ISuperMan создайте объект `superMan`. Метод guessWho должен работать следующим образом: если в качестве строки в аргументе приходит любое значение кроме superman (в любом регистре), то следует вернуть предположение "It's a ${value}?", иначе "It's a ${value}!". 5 | 6 | ```typescript 7 | console.log(superMan.guessWho('bird')); // "It's a bird?"; 8 | console.log(superMan.guessWho('plane')); "It's a plane?"; 9 | console.log(superMan.guessWho('superman')); "It's a superman!"; 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/20-functions/20-optional-parameters-in-callbacks/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `map()` function. It should take as input an array of numbers and a callback, which will be used to convert each number in the array to another number. The function returns an array with new numbers: 3 | 4 | ```typescript 5 | map([3, 9], (n) => n - 3); 6 | // [0, 6] 7 | 8 | map([8, 9], (n) => n + 8); 9 | // [16, 17] 10 | ``` 11 | 12 | Function parameters: 13 | 14 | 1. Array of numbers 15 | 2. Anonymous function that takes a number as input and returns a number. This function has an optional parameter - the index of the element in the array 16 | 17 | ```typescript 18 | map([8, 9], (n, index) => index + n); 19 | // [8, 10] 20 | ``` 21 | -------------------------------------------------------------------------------- /modules/30-classes/30-class-extending/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `HttpError` class, which must inherit from the built-in `Error` class and accept the error code as the first parameter and `message` as the second. Also implement classes `NotFoundError`, `UnauthorisedError`, `ForbiddenError`. Each of them should inherit from the `HttpError` class and have the `status` property, which is equal to the error code and `message` - the message passed to the base class. The error codes are: `404`, `401`, `403`. 3 | 4 | ```typescript 5 | import { NotFoundError } from './errors'; 6 | 7 | const error = new NotFoundError('Not Found'); 8 | console.log(error.status); // 404 9 | console.log(error.message); // Not Found 10 | ``` 11 | -------------------------------------------------------------------------------- /modules/40-generics/40-many-parameters/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте описание обобщенного типа `MyMap`, который представляет собой аналог ассоциативного массива из JavaScript. Пример использования объекта этого типа: 3 | 4 | ```typescript 5 | const map: MyMap = ...; 6 | map.set('one', 1); 7 | map.set('two', 2); 8 | 9 | map.get('one'); // 1 10 | map.get('two'); // 2 11 | ``` 12 | 13 | Тип включает в себя два метода `set()` и `get()`. Первый метод принимает два дженерик-параметра: ключ и значение. Второй метод принимает ключ и возвращает значение. Значения хранятся внутри объекта в виде встроенного в JavaScript класса [Map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). 14 | -------------------------------------------------------------------------------- /modules/25-types/40-assignability/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Assignability 3 | tips: 4 | - > 5 | [Assignability in the old documentation](https://github.com/microsoft/TypeScript-New-Handbook/blob/master/reference/Assignability.md) 6 | - > 7 | [Assignability table in the official documentation](https://www.typescriptlang.org/docs/handbook/type-compatibility.html#any-unknown-object-void-undefined-null-and-never-assignability) 8 | definitions: 9 | - name: Types Compatibility 10 | description: | 11 | a set of rules on the basis of which, when analysing a data type, a decision is made about the possibility of replacing one data type with another in such a way that the replacement does not disrupt program execution. 12 | -------------------------------------------------------------------------------- /modules/40-generics/40-many-parameters/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a description of the generalized type `MyMap`, which is an analog of the associative array from JavaScript. Example of using an object of this type: 3 | 4 | ```typescript 5 | const map: MyMap = ...; 6 | map.set('one', 1); 7 | map.set('two', 2); 8 | 9 | map.get('one'); // 1 10 | map.get('two'); // 2 11 | ``` 12 | 13 | The type includes two methods `set()` and `get()`. The first method accepts two generic parameters: a key and a value. The second method accepts the key and returns the value. Values are stored inside the object as a JavaScript built-in class [Map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). 14 | -------------------------------------------------------------------------------- /modules/50-objects/35-mapping-modifiers/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Реализуйте функцию `deepFreeze()`, которая принимает на вход объект и делает его самого, его поля и все вложенные объекты неизменяемыми и возвращает этот объект. 2 | Предполагается что поля объекта и поля вложенных объектов не содержат массивы, только простые типы данных и объекты. 3 | 4 | ```typescript 5 | const user = deepFreeze({ 6 | name: 'John', 7 | password: '1q2w3e', 8 | token: 'test', 9 | }); 10 | 11 | user.name = 'Alex'; // Error: Cannot assign to 'name' because it is a read-only property. 12 | ``` 13 | 14 | Нужно использовать встроенный в JavaScript метод [Object.freeze()](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze). 15 | -------------------------------------------------------------------------------- /modules/40-generics/40-many-parameters/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import type MyMap from './index'; 4 | 5 | test('MyMap', () => { 6 | const map: MyMap = { 7 | values: new Map(), 8 | set(key, value) { 9 | this.values.set(key, value); 10 | }, 11 | get(key) { 12 | return this.values.get(key); 13 | }, 14 | }; 15 | 16 | map.set('one', 1); 17 | map.set('two', 2); 18 | 19 | expect(map.get('one')).toBe(1); 20 | expect(map.get('two')).toBe(2); 21 | expect(map.get('three')).toBe(undefined); 22 | 23 | expectTypeOf(map.set).parameters.toExtend<[string, number]>(); 24 | expectTypeOf(map.get).returns.toExtend(); 25 | }); 26 | -------------------------------------------------------------------------------- /modules/50-objects/35-mapping-modifiers/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `deepFreeze()` function that takes an object as input and makes it, its fields and all nested objects immutable and returns that object. 3 | Assume that the fields of the object and the fields of the nested objects do not contain arrays, only simple data types and objects. 4 | 5 | ```typescript 6 | const user = deepFreeze({ 7 | name: 'John', 8 | password: '1q2w3e', 9 | token: 'test', 10 | }); 11 | 12 | user.name = 'Alex'; // Error: Cannot assign to 'name' because it is a read-only property. 13 | ``` 14 | 15 | You need to use JavaScript's built-in [Object.freeze()](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) method. -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Anagrams are words that are made up of the same letters. For example: 3 | 4 | dusty — study 5 | elbow — below 6 | peach — cheap 7 | 8 | Implement the `filterAnagrams()` function that finds all anagrams of a word. The function accepts a source word and a list to check - an array. And the function returns an array of all anagrams. If there is no anagram in the list, an empty array is returned: 9 | 10 | ```typescript 11 | filterAnagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada']); 12 | // ['aabb', 'bbaa'] 13 | 14 | filterAnagrams('racer', ['crazer', 'carer', 'racar', 'caers', 'racer']); 15 | // ['carer', 'racer'] 16 | 17 | filterAnagrams('laser', ['lazing', 'lazy', 'lacer']); 18 | // [] 19 | ``` 20 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Анаграммы — это слова, которые состоят из одинаковых букв. Например: 3 | 4 | спаниель — апельсин 5 | карат — карта — катар 6 | топор — ропот — отпор 7 | 8 | Реализуйте функцию `filterAnagrams()`, которая находит все анаграммы слова. Функция принимает исходное слово и список для проверки — массив. А возвращает функция массив всех анаграмм. Если в списке нет анаграммы, то возвращается пустой массив: 9 | 10 | ```typescript 11 | filterAnagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada']); 12 | // ['aabb', 'bbaa'] 13 | 14 | filterAnagrams('racer', ['crazer', 'carer', 'racar', 'caers', 'racer']); 15 | // ['carer', 'racer'] 16 | 17 | filterAnagrams('laser', ['lazing', 'lazy', 'lacer']); 18 | // [] 19 | ``` 20 | -------------------------------------------------------------------------------- /modules/25-types/40-assignability/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Присвоение значения 3 | tips: 4 | - > 5 | [Assignability в старой 6 | документации](https://github.com/microsoft/TypeScript-New-Handbook/blob/master/reference/Assignability.md) 7 | - > 8 | [Таблица assignability в официальной 9 | документации](https://www.typescriptlang.org/docs/handbook/type-compatibility.html#any-unknown-object-void-undefined-null-and-never-assignability) 10 | definitions: 11 | - name: Совместимость типов (Types Compatibility) 12 | description: > 13 | совокупность правил, на основе которых при анализе типа данных принимается 14 | решение о возможности заменить один тип данных другим таким образом, чтобы 15 | замена не нарушила выполнение программы. 16 | -------------------------------------------------------------------------------- /modules/10-basics/90-modules/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Система модулей 3 | tips: 4 | - > 5 | [Официальная документация про 6 | модули](https://www.typescriptlang.org/docs/handbook/modules.html) 7 | - > 8 | [Как происходит Module 9 | Resolution](https://www.typescriptlang.org/docs/handbook/module-resolution.html#how-nodejs-resolves-modules) 10 | - > 11 | [О модулях в JavaScript на 12 | Hexlet](https://ru.hexlet.io/courses/programming-basics/lessons/modules/theory_unit) 13 | - > 14 | [Официальная документация про 15 | Namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html) 16 | - > 17 | [Метод 18 | includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) 19 | -------------------------------------------------------------------------------- /modules/30-classes/15-class-as-types/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `CustomFile` class, which constructor is passed a file name and size in bytes or another file. Within the class, define an `toString()` method that should return a formatted string in the format `(copy) ( bytes)`. `(copy)` should only be output if the file is a copy of another file. 3 | 4 | ```typescript 5 | const file = new CustomFile({ name: 'open-world.jpeg', size: 1000 }); 6 | console.log(file.toString()); // open-world.jpeg (1000 bytes) 7 | 8 | const file2 = new CustomFile(file); 9 | console.log(file2.toString()); // (copy) open-world.jpeg (1000 bytes) 10 | 11 | const file3 = new CustomFile(file2); 12 | console.log(file2.toString()); // (copy) open-world.jpeg (1000 bytes) 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/35-interfaces/20-interface-using/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | import { type IBird, type IPlane, type ISuperman, superMan } from './index'; 3 | 4 | test('guess who', () => { 5 | expect(superMan.guessWho('bird')).toBe("It's a bird?"); 6 | expect(superMan.guessWho('plane')).toBe("It's a plane?"); 7 | expect(superMan.guessWho('SupErMan')).toBe("It's a SupErMan!"); 8 | }); 9 | 10 | test('Types check', () => { 11 | expectTypeOf().toExtend(); 12 | expectTypeOf().toExtend(); 13 | expectTypeOf(superMan).toExtend(); 14 | 15 | expect(superMan).toHaveProperty('canFly'); 16 | expect(superMan).toHaveProperty('isLiving'); 17 | expect(superMan).toHaveProperty('canCarryPeople'); 18 | }); 19 | -------------------------------------------------------------------------------- /modules/50-objects/30-mapped-types/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Mapped Types 3 | tips: 4 | - > 5 | [Official documentation on Mapped 6 | Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) 7 | - > 8 | [Official documentation on 9 | keyof](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types) 10 | - > 11 | [Understanding "keyof 12 | typeof"](https://www.huy.rocks/everyday/04-14-2022-typescript-understanding-keyof-typeof-) 13 | - > 14 | [Official documentation on 15 | Pick](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys) 16 | - > 17 | [Official documentation on 18 | Omit](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys) 19 | -------------------------------------------------------------------------------- /modules/30-classes/15-class-as-types/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте класс `CustomFile`, в конструктор которого передается имя файла и размер в байтах или другой файл. Внутри класса определите метод `toString()`, который должен вернуть форматированную строку в формате `(copy) ( bytes)`. `(copy)` должно выводиться только в том случае, если файл является копией другого файла. 3 | 4 | ```typescript 5 | const file = new CustomFile({ name: 'open-world.jpeg', size: 1000 }); 6 | console.log(file.toString()); // open-world.jpeg (1000 bytes) 7 | 8 | const file2 = new CustomFile(file); 9 | console.log(file2.toString()); // (copy) open-world.jpeg (1000 bytes) 10 | 11 | const file3 = new CustomFile(file2); 12 | console.log(file2.toString()); // (copy) open-world.jpeg (1000 bytes) 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Structural typing 3 | tips: 4 | - | 5 | [Structural typing in official documentation](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#structural-typing) 6 | - | 7 | [Nominal typing in TS](https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/) 8 | - | 9 | [Product Type](https://en.wikipedia.org/wiki/Product_type) 10 | - | 11 | [Tagged Union](https://en.wikipedia.org/wiki/Tagged_union) 12 | definitions: 13 | - name: Structural typing 14 | description: | 15 | a principle that defines compatibility of types based on their description (structure). A variable of type `A` may also be used where type `B` is expected, if it has the same or broader structure. 16 | -------------------------------------------------------------------------------- /modules/50-objects/35-mapping-modifiers/en/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Mapping Modifiers 3 | tips: 4 | - > 5 | [Official documentation on Mapped 6 | Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) 7 | - > 8 | [Official documentation on Mapping 9 | Modifiers](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#mapping-modifiers) 10 | - > 11 | [Official documentation on 12 | Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) 13 | - > 14 | [Official documentation on 15 | Required](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) 16 | - > 17 | [Official documentation on 18 | Readonly](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) 19 | -------------------------------------------------------------------------------- /modules/25-types/70-variability/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `applyTransactions(wallet)` и типы `Transaction`, `Wallet`. `Wallet` содержит список транзакций в виде массива элементов типа `Transaction` и числовой баланс. `Transaction` содержит метод `apply`, который принимает баланс и возвращает новый баланс. 3 | 4 | Функция `applyTransactions(wallet)` должна принимать аргумент типа `Wallet` и возвращать баланс после применения всего списка транзакций. В случае ошибки в одной из транзакций должно вернуться изначальный баланс, и не продолжать применять транзакции. 5 | 6 | ```typescript 7 | const wallet: Wallet = { 8 | transactions: [ 9 | { 10 | apply: (amount) => amount + 1, 11 | }, 12 | ], 13 | balance: 0 14 | } 15 | 16 | console.log(applyTransactions(wallet)) // 1 17 | ``` 18 | -------------------------------------------------------------------------------- /modules/25-types/25-literal-types/index.ts: -------------------------------------------------------------------------------- 1 | type Turtle = 'turtle' | null; 2 | 3 | type Game = { 4 | makeTurn: (direction: 'left' | 'right') => void; 5 | state: Array; 6 | }; 7 | 8 | const startGame = (): Game => { 9 | const state: Array = ['turtle', null, null, null, null]; 10 | 11 | // BEGIN 12 | const makeTurn = (direction: 'left' | 'right'): void => { 13 | const turtleIndex = state.indexOf('turtle'); 14 | const nextIndex = direction === 'left' ? turtleIndex - 1 : turtleIndex + 1; 15 | 16 | if (nextIndex < 0 || nextIndex > 4) { 17 | throw new Error('Out of bounds'); 18 | } 19 | 20 | state[turtleIndex] = null; 21 | state[nextIndex] = 'turtle'; 22 | }; 23 | // END 24 | 25 | return { makeTurn, state }; 26 | }; 27 | 28 | export default startGame; 29 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import handleData, { type DataState, LoadingStatus } from './index'; 3 | 4 | test('handleData', () => { 5 | const loading: DataState = { status: LoadingStatus.Loading }; 6 | expect(handleData(loading)).toBe('loading...'); 7 | 8 | const success: DataState = { status: LoadingStatus.Success, data: 42 }; 9 | expect(handleData(success)).toBe('42'); 10 | 11 | const error: DataState = { 12 | status: LoadingStatus.Error, 13 | error: new Error('error'), 14 | }; 15 | expect(handleData(error)).toBe('error'); 16 | 17 | const unknown = { status: 'unknown' }; 18 | // @ts-expect-error type '{ status: 'unknown' }' is not assignable to type 'DataState'. 19 | expect(handleData(unknown)).toBe('unknown'); 20 | }); 21 | -------------------------------------------------------------------------------- /modules/50-objects/30-mapped-types/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Сопоставление типов (Mapped Types) 3 | tips: 4 | - > 5 | [Официальная документация Mapped 6 | Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) 7 | - > 8 | [Официальная документация 9 | keyof](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types) 10 | - > 11 | [Understanding "keyof 12 | typeof"](https://www.huy.rocks/everyday/04-14-2022-typescript-understanding-keyof-typeof-) 13 | - > 14 | [Официальная документация 15 | Pick](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys) 16 | - > 17 | [Официальная документация 18 | Omit](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys) 19 | -------------------------------------------------------------------------------- /description.en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | title: | 3 | TypeScript course: free training for developers 4 | header: Typescript 5 | description: | 6 | In modern development, TypeScript has not just taken a firm place, but has replaced JavaScript in many places. Knowledge of TypeScript has become essential for any developer who works with either Node.js or the browser 7 | seo_description: | 8 | Sharpen your knowledge in a free Typescript course | Interactive exercises right in your browser | Free TypeScript course from CodeBasics 9 | 10 | keywords: 11 | - typescript 12 | - typoscript 13 | - type script 14 | - тайпскрипт 15 | - ts 16 | - тс 17 | - тайп скрипт 18 | - онлайн курс 19 | - бесплатный курс 20 | - программирование 21 | - code basics 22 | - online course 23 | - free course 24 | - programming 25 | -------------------------------------------------------------------------------- /modules/10-basics/40-named-functions/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | Реализуйте функцию `getHiddenCard()`. Она принимает на вход номер кредитки, который состоит из 16 цифр, в виде строки и возвращает его скрытую версию. Эта версия может использоваться на сайте для отображения. Например, если номер исходной карты был *2034399002125581*, то скрытая версия выглядит так: *\*\*\*\*5581*. 2 | 3 | Получается, функция заменяет первые 12 символов на звездочки. Количество звездочек регулируется вторым необязательным параметром. Значение по умолчанию — 4. 4 | 5 | ```typescript 6 | // Кредитка передается внутрь как строка 7 | getHiddenCard('1234567812345678', 2) // "**5678" 8 | getHiddenCard('1234567812345678', 3) // "***5678" 9 | getHiddenCard('1234567812345678') // "****5678" 10 | getHiddenCard('2034399002121100', 1) // "*1100" 11 | ``` 12 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/index.ts: -------------------------------------------------------------------------------- 1 | // BEGIN 2 | enum LoadingStatus { 3 | Loading = 'Loading', 4 | Success = 'Success', 5 | Error = 'Error', 6 | } 7 | 8 | type DataState = 9 | | { status: LoadingStatus.Loading } 10 | | { status: LoadingStatus.Success; data: number } 11 | | { status: LoadingStatus.Error; error: Error }; 12 | 13 | const handleData = (dataState: DataState): string => { 14 | switch (dataState.status) { 15 | case LoadingStatus.Loading: 16 | return 'loading...'; 17 | case LoadingStatus.Success: 18 | return String(dataState.data); 19 | case LoadingStatus.Error: 20 | return dataState.error.message; 21 | default: 22 | return 'unknown'; 23 | } 24 | }; 25 | // END 26 | 27 | export { type DataState, LoadingStatus }; 28 | export default handleData; 29 | -------------------------------------------------------------------------------- /modules/10-basics/40-named-functions/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the `getHiddenCard()` function. It takes as input a credit card number, which consists of 16 digits, as a string and returns a hidden version of it. This version can be used on the site for display. For example, if the original card number was *2034399002125581*, the hidden version looks like this: *\*\*\*\*5581*. 3 | 4 | The function replaces the first 12 characters with asterisks. The number of asterisks is controlled by the second optional parameter. The default value is 4. 5 | 6 | ```typescript 7 | // The credit card is passed as a string 8 | getHiddenCard('1234567812345678', 2) // "**5678" 9 | getHiddenCard('1234567812345678', 3) // "***5678" 10 | getHiddenCard('1234567812345678') // "****5678" 11 | getHiddenCard('2034399002121100', 1) // "*1100" 12 | ``` 13 | -------------------------------------------------------------------------------- /modules/50-objects/35-mapping-modifiers/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Модификаторы сопоставления типов (Mapping Modifiers) 3 | tips: 4 | - > 5 | [Официальная документация Mapped 6 | Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) 7 | - > 8 | [Официальная документация Mapping 9 | Modifiers](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#mapping-modifiers) 10 | - > 11 | [Официальная документация 12 | Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) 13 | - > 14 | [Официальная документация 15 | Required](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) 16 | - > 17 | [Официальная документация 18 | Readonly](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) 19 | -------------------------------------------------------------------------------- /description.ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | title: | 3 | Курс TypeScript: бесплатное обучение для разработчиков 4 | header: Typescript 5 | description: | 6 | В современной разработке TypeScript не просто занял прочное место, но и во многих местах заменил собой JavaScript. Знание TypeScript стало необходимым знанием любого разработчика, который работает либо с Node.js, либо с браузером 7 | seo_description: | 8 | Прокачайте свои знания в бесплатном курсе по Typescript | Интерактивные упражнения прямо в браузере | Бесплатный курс TypeScript от CodeBasics 9 | 10 | keywords: 11 | - typescript 12 | - typoscript 13 | - type script 14 | - тайпскрипт 15 | - ts 16 | - тс 17 | - тайп скрипт 18 | - онлайн курс 19 | - бесплатный курс 20 | - программирование 21 | - code basics 22 | - online course 23 | - free course 24 | - programming 25 | -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `getUserFriends(userResponseJSON, userId)`, которая принимает на вход JSON-строку и `userId` пользователя. JSON содержит массив пользователей `users` и массив друзей `friends` в виде пар `[userId, userId]`. Функция возвращает список друзей пользователя по переданному `userId``. 3 | 4 | Если пользователь с указанным id не найден, то функция должна вернуть пустой массив. 5 | 6 | ```typescript 7 | const userJson = JSON.stringify({ 8 | users: [ 9 | { id: 1, name: 'John', age: 20 }, 10 | { id: 2, name: 'Mary', age: 21 }, 11 | ], 12 | friends: [ 13 | [1, 2], 14 | ], 15 | }); 16 | 17 | getUserFriends(userJson, 1); // [{ id: 2, name: 'Mary', age: 21 }] 18 | getUserFriends(userJson, 2); // [{ id: 1, name: 'John', age: 20 }] 19 | getUserFriends(userJson, 3); // [] 20 | ``` 21 | -------------------------------------------------------------------------------- /modules/25-types/70-variability/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement the function `applyTransactions(wallet)` and types `Transaction`, `Wallet`. `Wallet` contains a list of transactions as an array of elements of type `Transaction` and a numeric balance. `Transaction` contains an `apply` method that accepts a balance and returns a new balance. 3 | 4 | The `applyTransactions(wallet)` function must take an argument of type `Wallet` and return the balance after the entire transaction list has been applied. In case of an error in one of the transactions, it should return the original balance and not continue applying transactions. 5 | 6 | ```typescript 7 | const wallet: Wallet = { 8 | transactions: [ 9 | { 10 | apply: (amount) => amount + 1, 11 | }, 12 | ], 13 | balance: 0 14 | } 15 | 16 | console.log(applyTransactions(wallet)) // 1 17 | ``` 18 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Опишите тип состояния `DataState` и перечисление `LoadingStatus`. Затем реализуйте функцию `handleData()`, которая принимает на вход `DataState` и возвращает строку в зависимости от состояния: `loading...` при `LoadingStatus.loading`, `error` при `LoadingStatus.error`, строку из числового поля `data` при `LoadingStatus.success`. Если статус не входит в перечисление, функция возвращает `unknown`. 3 | 4 | ```typescript 5 | const loading: DataState = { status: LoadingStatus.Loading }; 6 | console.log(handleData(loading)); // loading... 7 | 8 | const error: DataState = { status: LoadingStatus.Error, error: new Error('error') }; 9 | console.log(handleData(error)); // error 10 | 11 | const success: DataState = { status: LoadingStatus.Success, data: 42 }; 12 | console.log(handleData(success)); // 42 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/40-generics/50-async-functions/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | import asyncMap from './index'; 3 | 4 | test('asyncMap', async () => { 5 | const result = await asyncMap( 6 | [Promise.resolve(1), Promise.resolve(2)], 7 | (item) => item * 2, 8 | ); 9 | expect(result).toEqual([2, 4]); 10 | 11 | const result2 = await asyncMap( 12 | [Promise.resolve('one'), Promise.resolve('two'), Promise.resolve('three')], 13 | (item) => item.toUpperCase(), 14 | ); 15 | expect(result2).toEqual(['ONE', 'TWO', 'THREE']); 16 | 17 | const result3 = await asyncMap( 18 | [Promise.resolve(1), Promise.resolve(2)], 19 | (item, index) => item * index, 20 | ); 21 | expect(result3).toEqual([0, 2]); 22 | 23 | expectTypeOf(result).toExtend(); 24 | expectTypeOf(result2).toExtend(); 25 | }); 26 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/en/README.md: -------------------------------------------------------------------------------- 1 | 2 | As is tradition, we'll start by writing a 'Hello, World!' program. The program will print the following text: 3 | 4 | ```text 5 | Hello, World! 6 | ``` 7 | 8 | To print something, you need to give computer a special command. In TypeScript, we use console.log(). 9 | 10 | ## Launch special features 11 | 12 | The TypeScript compiler works slower than needed for code-basics exercises. Because of this, TypeScript errors in this course are only shown in the editor itself - highlighted in red and revealed when you hover. 13 | 14 | During the exercise execution, only JavaScript errors are shown. This means that you should make sure that there are no red underscores in the editor before checking the code. 15 | 16 | If you want to test the examples on your computer for correct behavior, make sure you use the `--strict` flag. 17 | -------------------------------------------------------------------------------- /modules/10-basics/55-objects/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import isComplete from './index'; 4 | 5 | test('function', () => { 6 | const course1 = { 7 | name: 'Java', 8 | lessons: ['variables', 'functions', 'conditions'], 9 | }; 10 | expect(isComplete(course1)).toBe(false); 11 | 12 | const course2 = { 13 | name: 'Java', 14 | lessons: ['variables', 'functions', 'conditions', 'loops'], 15 | }; 16 | expect(isComplete(course2)).toBe(true); 17 | 18 | const course3 = { 19 | name: 'Java', 20 | lessons: ['variables', 'functions', 'conditions', 'loops', 'cringe'], 21 | }; 22 | expect(isComplete(course3)).toBe(true); 23 | 24 | expectTypeOf(isComplete).returns.toExtend(); 25 | expectTypeOf(isComplete) 26 | .parameter(0) 27 | .toExtend<{ name: string; lessons: string[] }>(); 28 | }); 29 | -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a function `getUserFriends(userResponseJSON, userId)` that takes a JSON string and the `userId` of a user as input. The JSON contains an array of users `users` and with an array of friends `friends` as `[userId, userId]` pairs. The function returns the list of user's friends by the passed `userId`. 3 | 4 | If the user with the specified ID is not found, the function should return an empty array. 5 | 6 | ```typescript 7 | const userJson = JSON.stringify({ 8 | users: [ 9 | { id: 1, name: 'John', age: 20 }, 10 | { id: 2, name: 'Mary', age: 21 }, 11 | ], 12 | friends: [ 13 | [1, 2], 14 | ], 15 | }); 16 | 17 | getUserFriends(userJson, 1); // [{ id: 2, name: 'Mary', age: 21 }] 18 | getUserFriends(userJson, 2); // [{ id: 1, name: 'John', age: 20 }] 19 | getUserFriends(userJson, 3); // [] 20 | ``` 21 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Describe the `DataState` state type and the `LoadingStatus` enumeration. Then implement a `handleData()` function that takes `DataState` as input and returns a string depending on the state: `loading...` at `LoadingStatus.loading`, `error` at `LoadingStatus.error`, a string from the numeric field `data` at `LoadingStatus.success`. If the status is not included in the enumeration, the function returns `unknown`. 3 | 4 | ```typescript 5 | const loading: DataState = { status: LoadingStatus.Loading }; 6 | console.log(handleData(loading)); // loading... 7 | 8 | const error: DataState = { status: LoadingStatus.Error, error: new Error('error') }; 9 | console.log(handleData(error)); // error 10 | 11 | const success: DataState = { status: LoadingStatus.Success, data: 42 }; 12 | console.log(handleData(success)); // 42 13 | ``` 14 | -------------------------------------------------------------------------------- /modules/10-basics/10-hello-world/ru/README.md: -------------------------------------------------------------------------------- 1 | По традиции начнем с написания программы 'Hello, World!'. Эта программа будет выводить на экран текст: 2 | 3 | ```text 4 | Hello, World! 5 | ``` 6 | 7 | Чтобы вывести что-то на экран, нужно дать компьютеру специальную команду. В языке TypeScript такая команда — `console.log()`. 8 | 9 | ## Особенности запуска 10 | 11 | Компилятор TypeScript медленнее, чем нужно для отработки практики на code-basics. Из-за этого ошибки TypeScript в этом курсе показываются только в самом редакторе — подсвечиваются красным и раскрываются при наведении. 12 | 13 | Во время запуска практики показываются только ошибки JavaScript. Это значит, что перед проверкой кода нужно убедиться, что в редакторе нет красных подчеркиваний. 14 | 15 | Если вы хотите проверить примеры у себя на компьютере для корректного поведения, убедитесь, что используете флаг `--strict`. 16 | -------------------------------------------------------------------------------- /modules/50-objects/45-record/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a `createAccessChecker()` function that takes an object with role permissions as input and returns a function that checks whether a user has access to a resource. 3 | 4 | ```typescript 5 | type UserRole = 'admin' | 'user' | 'guest'; 6 | type UserResource = 'document' | 'user' | 'adminPanel'; 7 | 8 | const userRolePermissions: Record> = { 9 | admin: ['document', 'user', 'adminPanel'], 10 | user: ['document', 'user'], 11 | guest: ['document'], 12 | }; 13 | 14 | const checkUserAccess = createAccessChecker(userRolePermissions); 15 | 16 | const isAdminAllowed = checkUserAccess('admin', 'adminPanel'); 17 | console.log(isAdminAllowed); // => true 18 | 19 | const isUserAllowed = checkUserAccess('user', 'adminPanel'); 20 | console.log(isUserAllowed); // => false 21 | ``` 22 | -------------------------------------------------------------------------------- /modules/25-types/60-structural-typing/ru/data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Структурная типизация 3 | tips: 4 | - > 5 | [Structural typing в официальной 6 | документации](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#structural-typing) 7 | - > 8 | [Как получить номинативную типизацию в 9 | TS](https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/) 10 | - | 11 | [Тип-произведение](https://en.wikipedia.org/wiki/Product_type) 12 | - | 13 | [Тип-сумма](https://en.wikipedia.org/wiki/Tagged_union) 14 | definitions: 15 | - name: Структурная типизация 16 | description: > 17 | принцип, который определяет совместимость типов на основе их описания 18 | (структуры). Переменная типа `A` также может использоваться в том месте, 19 | где ожидается тип `B`, если обладает той же или более широкой структурой. 20 | -------------------------------------------------------------------------------- /modules/40-generics/30-generic-functions/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте описание обобщенного типа `MyArray`, который представляет аналог массива из JavaScript. Пример использования объекта этого типа: 3 | 4 | ```typescript 5 | const coll: MyArray = ...; 6 | coll.push(1); // 1 7 | coll.push(10); // 2 8 | coll.push(99); // 3 9 | 10 | const newColl = coll.filter((value) => value % 2 == 0); 11 | console.log(newColl.items); // [10] 12 | ``` 13 | 14 | Тип включает в себя два метода: [push()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) и [filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), совпадающие по сигнатуре с методами Array. Данные внутри должны храниться в свойстве `items`. Для `push()` примем соглашение, что метод принимает только один параметр. Игнорируйте остальные параметры. 15 | -------------------------------------------------------------------------------- /modules/50-objects/45-record/ru/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Реализуйте функцию `createAccessChecker()`, которая принимает на вход объект с разрешениями для ролей и возвращает функцию, проверяющую, есть ли у пользователя доступ к ресурсу. 3 | 4 | ```typescript 5 | type UserRole = 'admin' | 'user' | 'guest'; 6 | type UserResource = 'document' | 'user' | 'adminPanel'; 7 | 8 | const userRolePermissions: Record> = { 9 | admin: ['document', 'user', 'adminPanel'], 10 | user: ['document', 'user'], 11 | guest: ['document'], 12 | }; 13 | 14 | const checkUserAccess = createAccessChecker(userRolePermissions); 15 | 16 | const isAdminAllowed = checkUserAccess('admin', 'adminPanel'); 17 | console.log(isAdminAllowed); // => true 18 | 19 | const isUserAllowed = checkUserAccess('user', 'adminPanel'); 20 | console.log(isUserAllowed); // => false 21 | ``` 22 | -------------------------------------------------------------------------------- /modules/40-generics/30-generic-functions/en/EXERCISE.md: -------------------------------------------------------------------------------- 1 | 2 | Implement a description of a generic `MyArray` type that represents an analog of an array from JavaScript. Example of using an object of this type: 3 | 4 | ```typescript 5 | const coll: MyArray = ...; 6 | coll.push(1); // 1 7 | coll.push(10); // 2 8 | coll.push(99); // 3 9 | 10 | const newColl = coll.filter((value) => value % 2 == 0); 11 | console.log(newColl.items); // [10] 12 | ``` 13 | 14 | The type includes two methods: [push()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) and [filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), matching the signature of Array methods. The data inside should be stored in the `items` property. For `push()`, accept the convention that the method takes only one parameter. Ignore the other parameters. 15 | -------------------------------------------------------------------------------- /modules/50-objects/30-mapped-types/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import sanitize from './index'; 4 | 5 | test('sanitize', () => { 6 | const obj = { 7 | name: 'John', 8 | age: 30, 9 | password: '123456', 10 | }; 11 | 12 | expect(sanitize(obj, ['name', 'age'])).toEqual({ 13 | password: '123456', 14 | }); 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 17 | const user = sanitize(obj, ['password']); 18 | 19 | expectTypeOf(user).toExtend<{ name: string; age: number }>(); 20 | 21 | const params = { 22 | page: 1, 23 | limit: 10, 24 | filter: { 25 | name: 'John', 26 | }, 27 | }; 28 | 29 | const query = sanitize(params, ['filter']); 30 | expect(query).toEqual({ 31 | page: 1, 32 | limit: 10, 33 | }); 34 | 35 | expectTypeOf(query).toExtend<{ page: number; limit: number }>(); 36 | }); 37 | -------------------------------------------------------------------------------- /modules/30-classes/20-members-visibility/index.ts: -------------------------------------------------------------------------------- 1 | type CustomFileOptions = { 2 | name: string; 3 | size: number; 4 | }; 5 | 6 | class CustomFile { 7 | private name: string; 8 | 9 | private size: number; 10 | 11 | constructor(options: CustomFileOptions) { 12 | this.name = options.name; 13 | this.size = options.size; 14 | } 15 | 16 | protected toString() { 17 | return `${this.name} (${this.size} bytes)`; 18 | } 19 | } 20 | 21 | // BEGIN 22 | class ImageCustomFile extends CustomFile { 23 | private width: number; 24 | 25 | private height: number; 26 | 27 | constructor(options: CustomFileOptions & { width: number; height: number }) { 28 | super(options); 29 | this.width = options.width; 30 | this.height = options.height; 31 | } 32 | 33 | toString() { 34 | return `${super.toString()} ${this.width}x${this.height}`; 35 | } 36 | } 37 | // END 38 | 39 | export default ImageCustomFile; 40 | -------------------------------------------------------------------------------- /modules/50-objects/45-record/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import createAccessChecker from './index'; 4 | 5 | test('function', () => { 6 | type UserRole = 'admin' | 'user' | 'guest'; 7 | type UserResource = 'document' | 'user' | 'adminPanel'; 8 | 9 | const userRolePermissions: Record> = { 10 | admin: ['document', 'user', 'adminPanel'], 11 | user: ['document', 'user'], 12 | guest: ['document'], 13 | }; 14 | 15 | const checkUserAccess = createAccessChecker( 16 | userRolePermissions, 17 | ); 18 | 19 | const isAdminAllowed = checkUserAccess('admin', 'adminPanel'); 20 | expect(isAdminAllowed).toBe(true); 21 | 22 | const isUserAllowed = checkUserAccess('user', 'adminPanel'); 23 | expect(isUserAllowed).toBe(false); 24 | 25 | expectTypeOf(checkUserAccess).parameters.toExtend<[UserRole, UserResource]>(); 26 | }); 27 | -------------------------------------------------------------------------------- /modules/25-types/25-literal-types/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import startGame from './index'; 4 | 5 | test('startTurtleGame', () => { 6 | const { makeTurn, state } = startGame(); 7 | 8 | expect(state).toEqual(['turtle', null, null, null, null]); 9 | 10 | expect(() => makeTurn('left')).toThrow(); 11 | expect(state).toEqual(['turtle', null, null, null, null]); 12 | 13 | makeTurn('right'); 14 | expect(state).toEqual([null, 'turtle', null, null, null]); 15 | 16 | makeTurn('right'); 17 | makeTurn('right'); 18 | expect(state).toEqual([null, null, null, 'turtle', null]); 19 | 20 | makeTurn('right'); 21 | expect(state).toEqual([null, null, null, null, 'turtle']); 22 | 23 | expect(() => makeTurn('right')).toThrow(); 24 | 25 | makeTurn('left'); 26 | expect(state).toEqual([null, null, null, 'turtle', null]); 27 | 28 | expectTypeOf(makeTurn).returns.toExtend(); 29 | }); 30 | -------------------------------------------------------------------------------- /modules/30-classes/30-class-extending/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import { 3 | ForbiddenError, 4 | HttpError, 5 | NotFoundError, 6 | UnauthorizedError, 7 | } from './index'; 8 | 9 | test('HttpError', () => { 10 | const error = new HttpError(500, 'Internal Server Error'); 11 | expect(error.status).toBe(500); 12 | expect(error.message).toBe('Internal Server Error'); 13 | 14 | const forbiddenError = new ForbiddenError('Access denied'); 15 | expect(forbiddenError.status).toBe(403); 16 | expect(forbiddenError.message).toBe('Access denied'); 17 | 18 | const notFoundError = new NotFoundError('Not found'); 19 | expect(notFoundError.status).toBe(404); 20 | expect(notFoundError.message).toBe('Not found'); 21 | 22 | const unauthorizedError = new UnauthorizedError('Unauthorized'); 23 | expect(unauthorizedError.status).toBe(401); 24 | expect(unauthorizedError.message).toBe('Unauthorized'); 25 | }); 26 | -------------------------------------------------------------------------------- /modules/40-generics/30-generic-functions/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import type MyArray from './index'; 4 | 5 | test('MyArray', () => { 6 | const coll: MyArray = { 7 | items: [], 8 | push(value) { 9 | return this.items.push(value); 10 | }, 11 | filter(callback) { 12 | const newItems = this.items.filter(callback); 13 | return { ...this, items: newItems }; 14 | }, 15 | }; 16 | 17 | expect(coll.push(1)).toBe(1); 18 | expect(coll.push(2)).toBe(2); 19 | expect(coll.push(5)).toBe(3); 20 | 21 | expectTypeOf(coll.push).parameters.toExtend<[number]>(); 22 | 23 | const coll1: MyArray = { 24 | items: [], 25 | push(value) { 26 | return this.items.push(value); 27 | }, 28 | filter(callback) { 29 | const newItems = this.items.filter(callback); 30 | return { ...this, items: newItems }; 31 | }, 32 | }; 33 | 34 | expectTypeOf(coll1.push).parameters.toExtend<[string]>(); 35 | }); 36 | -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/index.ts: -------------------------------------------------------------------------------- 1 | type User = { 2 | id: number; 3 | name: string; 4 | age: number; 5 | }; 6 | 7 | type Friends = [number, number]; 8 | 9 | export type UserResponse = { 10 | users: User[]; 11 | friends: Friends[]; 12 | }; 13 | 14 | // BEGIN 15 | const defaultUser = { id: 0, name: '', age: 0 }; 16 | const getUserFriends = (userResponseJSON: string, userId: number): User[] => { 17 | const userResponse = JSON.parse(userResponseJSON) as UserResponse; 18 | 19 | return userResponse.friends 20 | .map(([ownerId, friendId]: Friends): User => { 21 | if (!(userId === ownerId || userId === friendId)) return defaultUser; 22 | const searchId = ownerId === userId ? friendId : ownerId; 23 | const friend: User | undefined = userResponse.users.find( 24 | ({ id }) => id === searchId, 25 | ); 26 | 27 | return friend === undefined ? defaultUser : friend; 28 | }) 29 | .filter((user: User) => user.id > 0); 30 | }; 31 | // END 32 | 33 | export default getUserFriends; 34 | -------------------------------------------------------------------------------- /modules/30-classes/20-members-visibility/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import ImageCustomFile from './index'; 3 | 4 | test('ImageCustomFile', () => { 5 | const imageCustomFile = new ImageCustomFile({ 6 | name: 'image.png', 7 | size: 100, 8 | width: 200, 9 | height: 300, 10 | }); 11 | 12 | expect(imageCustomFile.toString()).toBe('image.png (100 bytes) 200x300'); 13 | 14 | const imageCustomFile2 = new ImageCustomFile({ 15 | name: 'image2.png', 16 | size: 400, 17 | width: 500, 18 | height: 600, 19 | }); 20 | 21 | expect(imageCustomFile2.toString()).toBe('image2.png (400 bytes) 500x600'); 22 | // @ts-expect-error - private property 23 | expect(imageCustomFile2.name).toBe('image2.png'); 24 | // @ts-expect-error - private property 25 | expect(imageCustomFile2.size).toBe(400); 26 | // @ts-expect-error - private property 27 | expect(imageCustomFile2.width).toBe(500); 28 | // @ts-expect-error - private property 29 | expect(imageCustomFile2.height).toBe(600); 30 | }); 31 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/en/README.md: -------------------------------------------------------------------------------- 1 | 2 | Filling properties from constructor parameters is a common task when working with classes. That's why TypeScript has added a special syntax that allows you to do this automatically: 3 | 4 | ```typescript 5 | class SomeClass { 6 | constructor(public one: number, private two: string) {} 7 | 8 | get three(): string { 9 | return `${this.one} ${this.two}`; 10 | } 11 | } 12 | ``` 13 | 14 | This code does the same thing as this code: 15 | 16 | ```typescript 17 | class SomeClass { 18 | public one: number; 19 | 20 | private two: string; 21 | 22 | constructor(one: number, two: string) { 23 | this.one = one; 24 | this.two = two; 25 | } 26 | 27 | get three(): string { 28 | return `${this.one} ${this.two}`; 29 | } 30 | } 31 | ``` 32 | 33 | The new syntax makes it possible not to duplicate the code for filling properties from parameters and makes it more concise. If there is some logic in the constructor, the properties still need to be filled in manually. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # exercises-typescript 2 | 3 | [![GitHub Actions Status](../../workflows/Docker/badge.svg)](../../actions) 4 | 5 | ## How to contribute 6 | 7 | * Discuss the project on Telegram: https://t.me/hexletcommunity/12 8 | 9 | ### Requirements 10 | 11 | * docker 12 | * docker compose V2 13 | * make 14 | 15 | ## Develop 16 | 17 | ```bash 18 | # setup 19 | make 20 | # run 21 | make compose 22 | # check 23 | make ci-check 24 | 25 | # run tests 26 | make compose-test 27 | 28 | # run linters and validators 29 | make code-lint 30 | make compose-description-lint 31 | make compose-schema-validate 32 | ``` 33 | 34 | ## 35 | [![Hexlet Ltd. logo](https://raw.githubusercontent.com/Hexlet/assets/master/images/hexlet_logo128.png)](https://hexlet.io/?utm_source=github&utm_medium=link&utm_campaign=exercises-typescript) 36 | 37 | This repository is created and maintained by the team and the community of Hexlet, an educational project. [Read more about Hexlet](https://hexlet.io/?utm_source=github&utm_medium=link&utm_campaign=exercises-typescript). 38 | ## 39 | 40 | See most active contributors on [hexlet-friends](https://friends.hexlet.io/). 41 | -------------------------------------------------------------------------------- /modules/30-classes/23-parameter-properties/ru/README.md: -------------------------------------------------------------------------------- 1 | 2 | Заполнение свойств из параметров конструктора частая задача в работе с классами. Поэтому в TypeScript добавили специальный синтаксис, который позволяет делать это автоматически: 3 | 4 | ```typescript 5 | class SomeClass { 6 | constructor(public one: number, private two: string) {} 7 | 8 | get three(): string { 9 | return `${this.one} ${this.two}`; 10 | } 11 | } 12 | ``` 13 | 14 | Этот код делает то же самое, что и этот: 15 | 16 | ```typescript 17 | class SomeClass { 18 | public one: number; 19 | 20 | private two: string; 21 | 22 | constructor(one: number, two: string) { 23 | this.one = one; 24 | this.two = two; 25 | } 26 | 27 | get three(): string { 28 | return `${this.one} ${this.two}`; 29 | } 30 | } 31 | ``` 32 | 33 | 34 | 35 | Новый синтаксис позволяет не дублировать код заполнения свойств из параметров и делает его более лаконичным. Если в конструкторе есть какая-то логика, то свойства все равно нужно заполнять вручную. 36 | -------------------------------------------------------------------------------- /modules/25-types/70-variability/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | import applyTransactions, { type Wallet } from './index'; 3 | 4 | test('applyTransactions', () => { 5 | const wallet: Wallet = { 6 | balance: 100, 7 | transactions: [ 8 | { 9 | apply: (amount: number) => amount + 10, 10 | }, 11 | { 12 | apply: (amount: number) => amount - 20, 13 | }, 14 | { 15 | apply: (amount: number) => amount + 30, 16 | }, 17 | ], 18 | }; 19 | 20 | expect(applyTransactions(wallet)).toBe(120); 21 | expect(wallet.balance).toBe(100); 22 | 23 | const wallet2: Wallet = { 24 | balance: 10, 25 | transactions: [ 26 | { 27 | apply: (amount: number) => amount + 10, 28 | }, 29 | { 30 | apply: () => { 31 | throw new Error('Error'); 32 | }, 33 | }, 34 | { 35 | apply: (amount: number) => amount + 30, 36 | }, 37 | ], 38 | }; 39 | 40 | expect(applyTransactions(wallet2)).toBe(10); 41 | 42 | expectTypeOf(wallet2.transactions[0].apply).returns.toExtend(); 43 | }); 44 | -------------------------------------------------------------------------------- /modules/40-generics/20-generic-types/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import type MySet from './index'; 4 | 5 | test('function', () => { 6 | const s1: MySet = { 7 | items: [], 8 | has(value) { 9 | return this.items.includes(value); 10 | }, 11 | add(value) { 12 | this.items.push(value); 13 | return this.items.length; 14 | }, 15 | }; 16 | 17 | expect(s1.has(1)).toBe(false); 18 | s1.add(1); 19 | expect(s1.has(1)).toBe(true); 20 | 21 | expectTypeOf(s1.has).parameters.toExtend<[number]>(); 22 | expectTypeOf(s1.add).parameters.toExtend<[number]>(); 23 | }); 24 | 25 | test('function', () => { 26 | const s1: MySet = { 27 | items: [], 28 | has(value) { 29 | return this.items.includes(value); 30 | }, 31 | add(value) { 32 | this.items.push(value); 33 | return this.items.length; 34 | }, 35 | }; 36 | 37 | expect(s1.has('hexlet')).toBe(false); 38 | s1.add('hexlet'); 39 | expect(s1.has('hexlet')).toBe(true); 40 | 41 | expectTypeOf(s1.has).parameters.toExtend<[string]>(); 42 | expectTypeOf(s1.add).parameters.toExtend<[string]>(); 43 | }); 44 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/en/README.md: -------------------------------------------------------------------------------- 1 | 2 | In this lesson, we'll talk about arrays. TypeScript knows how to output their type, just as it does with primitive data types: 3 | 4 | ```typescript 5 | const fruits = ['banana', 'mango', 'apple']; 6 | // Everything works 7 | const upperFruits = fruits.map((name) => name.toUpperCase()); 8 | 9 | // Here, it does not 10 | // Property 'key' does not exist on type 'string'. 11 | const upperFruits = fruits.map((name) => name.key); 12 | ``` 13 | 14 | **Array** is a composite data type that is a container for another type. For example, the type “array of numbers” or “array of strings” are containers that contain strings or numbers. 15 | 16 | To denote such a type, square brackets are used: `number[]`, `string[]`. 17 | 18 | The definition of an array above could be written as follows: 19 | 20 | ```typescript 21 | const fruits: string[] = ['banana', 'mango', 'apple']; 22 | ``` 23 | 24 | This is also how types are described in function definitions: 25 | 26 | ```typescript 27 | function toUpperArray(items: string[]): string[] { 28 | return items.map((s) => s.toUpperCase()); 29 | } 30 | ``` 31 | 32 | In conclusion, arrays can be useful tools when working with data. -------------------------------------------------------------------------------- /modules/25-types/50-type-hierarcy/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import getUserFriends, { type UserResponse } from './index'; 3 | 4 | test('getUserFriends', () => { 5 | const userJson = JSON.stringify({ 6 | users: [ 7 | { id: 1, name: 'John', age: 20 }, 8 | { id: 2, name: 'Mary', age: 21 }, 9 | { id: 3, name: 'Peter', age: 22 }, 10 | { id: 4, name: 'Ann', age: 23 }, 11 | ], 12 | friends: [ 13 | [1, 2], 14 | [1, 3], 15 | [3, 2], 16 | ], 17 | }); 18 | 19 | const friends = getUserFriends(userJson, 10); 20 | expect(friends).toEqual([]); 21 | 22 | const friends1 = getUserFriends(userJson, 1); 23 | expect(friends1).toEqual([ 24 | { id: 2, name: 'Mary', age: 21 }, 25 | { id: 3, name: 'Peter', age: 22 }, 26 | ]); 27 | 28 | const friends2 = getUserFriends(userJson, 2); 29 | expect(friends2).toEqual([ 30 | { id: 1, name: 'John', age: 20 }, 31 | { id: 3, name: 'Peter', age: 22 }, 32 | ]); 33 | 34 | const friends3 = getUserFriends(userJson, 3); 35 | expect(friends3).toEqual([ 36 | { id: 1, name: 'John', age: 20 }, 37 | { id: 2, name: 'Mary', age: 21 }, 38 | ]); 39 | }); 40 | -------------------------------------------------------------------------------- /modules/10-basics/50-arrays/ru/README.md: -------------------------------------------------------------------------------- 1 | 2 | В этом уроке мы поговорим про массивы. TypeScript умеет выводить их тип, как и в случае с примитивными типами данных: 3 | 4 | ```typescript 5 | const fruits = ['banana', 'mango', 'apple']; 6 | // Все работает 7 | const upperFruits = fruits.map((name) => name.toUpperCase()); 8 | 9 | // А так уже нет 10 | // Property 'key' does not exist on type 'string'. 11 | const upperFruits = fruits.map((name) => name.key); 12 | ``` 13 | 14 | **Массив** — это составной тип данных, который представляет собой контейнер для другого типа. Например, тип «массив чисел» или «массив строк» — это контейнеры, содержащие в себе строки или числа. 15 | 16 | Чтобы обозначить такой тип, используются квадратные скобки: `number[]`, `string[]`. 17 | 18 | Определение массива выше можно было бы записать так: 19 | 20 | ```typescript 21 | const fruits: string[] = ['banana', 'mango', 'apple']; 22 | ``` 23 | 24 | Так же описываются типы в определении функций: 25 | 26 | ```typescript 27 | function toUpperArray(items: string[]): string[] { 28 | return items.map((s) => s.toUpperCase()); 29 | } 30 | ``` 31 | 32 | В заключении можно сказать, что массивы могут быть полезными инструментами при работе с данными. 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include /opt/basics/common/common.mk 2 | 3 | compose-setup: compose-build compose-install 4 | 5 | compose: 6 | docker compose up 7 | 8 | compose-build: 9 | docker compose pull 10 | docker compose build 11 | 12 | compose-install: 13 | docker compose run --rm exercises npm install 14 | 15 | compose-update: 16 | docker compose run --rm exercises npx ncu -u 17 | 18 | code-lint: 19 | npx @biomejs/biome check 20 | 21 | code-lint-fix: 22 | npx @biomejs/biome check --fix 23 | 24 | # compile: 25 | # @(for i in $$(find . -type f -name Main.java); do javac $$(dirname $$i)/*.java ; done) 26 | 27 | # clean: 28 | # @$$(find . -type f -name *.class -delete) 29 | 30 | compose-bash: 31 | docker compose run --rm exercises bash 32 | 33 | compose-test: 34 | docker compose run --rm exercises make test 35 | 36 | compose-description-lint: 37 | docker compose run --rm exercises make description-lint 38 | 39 | compose-schema-validate: 40 | docker compose run --rm exercises make schema-validate 41 | 42 | ci-check: 43 | docker compose --file docker-compose.yml build 44 | docker compose --file docker-compose.yml up --abort-on-container-exit 45 | 46 | test-fast: 47 | npx vitest 48 | 49 | update-deps: 50 | npx ncu -u 51 | -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/en/README.md: -------------------------------------------------------------------------------- 1 | 2 | In this lesson, we will learn about anonymous functions. Together with arrow functions, they are usually used in the same place where they are defined. Because of this, TypeScript can output the types of their parameters. 3 | 4 | To define anonymous functions, the type indication is omitted: 5 | 6 | ```typescript 7 | const fruits = ['banana', 'mango', 'apple']; 8 | const upperFruits = fruits.map((name) => name.toUpperCase()); 9 | // ['BANANA', 'MANGO', 'APPLE'] 10 | ``` 11 | 12 | This process is called **contextual typing** because the context of the function definition allows you to deduce the types of input parameters. As a result, the code looks identical to JavaScript code. 13 | 14 | If a function is defined out of context, the same rules apply as to named functions. Parameter types must be specified at the time of definition: 15 | 16 | ```typescript 17 | const toUpper = (name: string): string => name.toUpperCase(); 18 | const upperFruits = fruits.map(toUpper); 19 | ``` 20 | 21 | ## Conclusions 22 | 23 | In this lesson, we looked at how to define anonymous functions and use them in different contexts. Anonymous functions can make code more readable and understandable. -------------------------------------------------------------------------------- /modules/10-basics/45-anonymous-functions/ru/README.md: -------------------------------------------------------------------------------- 1 | 2 | В этом уроке мы познакомимся с анонимными функциями. Вместе со стрелочными они обычно используются в том же месте, где и определяются. Благодаря этому TypeScript может вывести типы их параметров. 3 | 4 | Чтобы определить анонимные функции, указание типов опускают: 5 | 6 | ```typescript 7 | const fruits = ['banana', 'mango', 'apple']; 8 | const upperFruits = fruits.map((name) => name.toUpperCase()); 9 | // ['BANANA', 'MANGO', 'APPLE'] 10 | ``` 11 | 12 | Этот процесс называется **контекстная типизация (contextual typing)**, так как контекст определения функции позволяет вывести типы входных параметров. В итоге код выглядит идентично коду на JavaScript. 13 | 14 | Если функция определяется вне контекста, то к ней применяются те же правила, что и к именованным функциям. То есть типы параметров должны быть заданы во время определения: 15 | 16 | ```typescript 17 | const toUpper = (name: string): string => name.toUpperCase(); 18 | const upperFruits = fruits.map(toUpper); 19 | ``` 20 | 21 | ## Выводы 22 | 23 | В этом уроке мы рассмотрели способ определения анонимных функций и использование их в различных контекстах. Анонимные функции могут сделать код более читаемым и понятным. 24 | -------------------------------------------------------------------------------- /modules/50-objects/35-mapping-modifiers/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from 'vitest'; 2 | 3 | import deepFreeze from './index'; 4 | 5 | test('deepFreeze', () => { 6 | const obj = { 7 | name: 'John', 8 | age: 30, 9 | location: { 10 | city: 'York', 11 | coordinates: { 12 | lat: 53.958, 13 | lon: -1.093, 14 | }, 15 | }, 16 | }; 17 | 18 | const user = deepFreeze(obj); 19 | 20 | expect(user).toEqual({ 21 | name: 'John', 22 | age: 30, 23 | location: { 24 | city: 'York', 25 | coordinates: { 26 | lat: 53.958, 27 | lon: -1.093, 28 | }, 29 | }, 30 | }); 31 | 32 | expect(() => { 33 | // @ts-expect-error Cannot assign read-only property. 34 | user.age = 20; 35 | }).toThrow(); 36 | 37 | expect(() => { 38 | // @ts-expect-error Cannot assign nested read-only property. 39 | user.location.city = 'London'; 40 | }).toThrow(); 41 | 42 | expectTypeOf(user).toExtend< 43 | Readonly<{ 44 | name: string; 45 | age: number; 46 | location: Readonly<{ 47 | city: string; 48 | coordinates: Readonly<{ 49 | lat: number; 50 | lon: number; 51 | }>; 52 | }>; 53 | }> 54 | >(); 55 | }); 56 | -------------------------------------------------------------------------------- /modules/30-classes/80-abstract-classes/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import Clock from './index'; 3 | 4 | test('Clock', () => { 5 | class Clock12 extends Clock { 6 | render(): string { 7 | const timeType = this.hours >= 12 ? 'PM' : 'AM'; 8 | 9 | let currentHour = this.hours > 12 ? this.hours - 12 : this.hours; 10 | if (timeType === 'AM' && this.hours === 0) { 11 | currentHour = 12; 12 | } 13 | 14 | const hours = currentHour.toString().padStart(2, '0'); 15 | const minutes = this.minutes.toString().padStart(2, '0'); 16 | return `${hours} : ${minutes} ${timeType}`; 17 | } 18 | } 19 | 20 | class Clock24 extends Clock { 21 | render(): string { 22 | return `${this.hours.toString().padStart(2, '0')} : ${this.minutes.toString().padStart(2, '0')}`; 23 | } 24 | } 25 | 26 | const clock121 = new Clock12(11, 59, 0); 27 | expect(clock121.render()).toBe('11 : 59 AM'); 28 | 29 | const clock12 = new Clock12(23, 59, 58); 30 | expect(clock12.render()).toBe('11 : 59 PM'); 31 | 32 | clock12.tick(); 33 | clock12.tick(); 34 | 35 | expect(clock12.render()).toBe('12 : 00 AM'); 36 | 37 | const clock24 = new Clock24(23, 59, 58); 38 | expect(clock24.render()).toBe('23 : 59'); 39 | 40 | clock24.tick(); 41 | clock24.tick(); 42 | 43 | expect(clock24.render()).toBe('00 : 00'); 44 | }); 45 | -------------------------------------------------------------------------------- /modules/30-classes/80-abstract-classes/en/README.md: -------------------------------------------------------------------------------- 1 | 2 | When we need to define a common behavior for several classes, it is convenient to use abstract classes, which we will explore in this lesson. 3 | 4 | Although abstract classes cannot be created directly, they can be inherited. They can also specify explicitly which method should be implemented in the inheritors: 5 | 6 | ```typescript 7 | abstract class CustomFile { 8 | protected name: string; 9 | 10 | protected size: number; 11 | 12 | constructor(name: string, size: number) { 13 | this.name = name; 14 | this.size = size; 15 | } 16 | 17 | sizeInKb(): number { 18 | return this.size / 1024; 19 | } 20 | } 21 | 22 | class ImageCustomFile extends CustomFile { 23 | constructor(name: string, size: number) { 24 | super(name, size); 25 | } 26 | } 27 | ``` 28 | 29 | To take the common part of code out of classes, abstract classes are actively used to build application architecture and frameworks. For example, React has a class `Component` which can be represented as an abstract class. We can't create it directly, but it requires inheritors to implement the `render` method. This allows us to create components that will be rendered on initialization: 30 | 31 | ```typescript 32 | abstract class Component { 33 | abstract render(): void; 34 | 35 | constructor() { 36 | this.render(); 37 | } 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "vcs": { 4 | "enabled": true, 5 | "clientKind": "git", 6 | "useIgnoreFile": true 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "includes": [ 11 | "**", 12 | "!**/*.json", 13 | "!**/*.css", 14 | "!**/*.scss", 15 | "!test/**", 16 | "!public/**", 17 | "!app/assets/builds/**", 18 | "!app/javascript/routes.*", 19 | "!app/javascript/types/serializers/**" 20 | ] 21 | }, 22 | "formatter": { 23 | "enabled": true, 24 | "indentStyle": "space" 25 | }, 26 | "linter": { 27 | "enabled": true, 28 | "rules": { 29 | "recommended": true, 30 | "complexity": { 31 | "noBannedTypes": "off" 32 | }, 33 | "correctness": { 34 | "noUnusedImports": "off", 35 | "noUnusedFunctionParameters": "off", 36 | "noUnusedVariables": "off" 37 | }, 38 | "suspicious": { 39 | "noExplicitAny": "off" 40 | }, 41 | "style": { 42 | "noNonNullAssertion": "off" 43 | } 44 | } 45 | }, 46 | "javascript": { 47 | "formatter": { 48 | "quoteStyle": "single" 49 | } 50 | }, 51 | "assist": { 52 | "enabled": true, 53 | "actions": { 54 | "source": { 55 | "organizeImports": "on" 56 | } 57 | } 58 | } 59 | } 60 | --------------------------------------------------------------------------------