├── TS_step_9 ├── form.ts ├── slider.ts └── index.ts ├── .gitignore ├── TS_Step_1 └── index.ts ├── TS_step_4 └── index.ts ├── TS_step_3 └── index.ts ├── Changelog.md ├── README.md ├── TS_Step_2 └── index.ts ├── TS_step_8 └── index.ts ├── TS_step_5 └── index.ts ├── TS_step_6 ├── index.ts ├── index.html └── tsconfig.json ├── TS_step_10 └── index.ts ├── TS_step_7 └── index.ts ├── TS_step_11 └── index.ts └── TS_step_12 └── index.ts /TS_step_9/form.ts: -------------------------------------------------------------------------------- 1 | interface IForm { 2 | login: string; 3 | password: string; 4 | } 5 | 6 | const validationData: Validation = { 7 | login: { isValid: false, errorMsg: "At least 3 characters" }, 8 | password: { isValid: true }, 9 | }; 10 | 11 | type Validation = { 12 | [P in keyof T]: { isValid: true } | { isValid: false; errorMsg: string }; 13 | }; 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /TS_Step_1/index.ts: -------------------------------------------------------------------------------- 1 | const currRate: string = "1.05"; 2 | 3 | const fetchCurr = (response: string): number => { 4 | const data: number = JSON.parse(response); 5 | return data; 6 | }; 7 | 8 | function transferEurToUsd( 9 | available: boolean, 10 | amount: number, 11 | commission: number 12 | ): void { 13 | if (available) { 14 | let res: number = fetchCurr(currRate) * amount * commission; 15 | console.log(res); 16 | // Или запись в элемент на странице вместо консоли 17 | } else { 18 | console.log("Сейчас обмен недоступен"); 19 | } 20 | } 21 | 22 | transferEurToUsd(true, 500, 1.05); 23 | -------------------------------------------------------------------------------- /TS_step_9/slider.ts: -------------------------------------------------------------------------------- 1 | interface ISlider { 2 | container?: string; 3 | numberOfSlides?: number; 4 | speed?: 300 | 500 | 700; 5 | direction?: "horizontal" | "vertical"; 6 | dots?: boolean; 7 | arrows?: boolean; 8 | animationName?: string; 9 | } 10 | 11 | type CustomSliderBase = Required>; 12 | interface ICustomSlider extends CustomSliderBase { 13 | speed: number; 14 | } 15 | 16 | function createSlider({ 17 | container = "", 18 | numberOfSlides = 1, 19 | speed = 300, 20 | direction = "horizontal", 21 | dots = true, 22 | arrows = true, 23 | }: ISlider = {}): void { 24 | console.log(container, numberOfSlides, speed, direction, dots, arrows); 25 | } 26 | 27 | createSlider(); 28 | 29 | // Все поля обязательны для заполнения 30 | const customSliderOptions: ICustomSlider = { 31 | container: "id", 32 | numberOfSlides: 4, 33 | speed: 1100, 34 | direction: "horizontal", 35 | dots: true, 36 | arrows: true, 37 | }; 38 | 39 | function createCustomSlider(options: ICustomSlider): void { 40 | if ("container" in options) { 41 | console.log(options); 42 | } 43 | } 44 | 45 | createCustomSlider(customSliderOptions); 46 | -------------------------------------------------------------------------------- /TS_step_4/index.ts: -------------------------------------------------------------------------------- 1 | enum TypesOfMedia { 2 | Video = "video", 3 | Audio = "audio", 4 | } 5 | 6 | enum FormatsOfMedia { 7 | MP4 = ".mp4", 8 | MOV = ".mov", 9 | MKV = ".mkv", 10 | FLV = ".flv", 11 | WEBM = ".webM", 12 | } 13 | 14 | interface MediaProps { 15 | name: string; 16 | type: TypesOfMedia; 17 | format: FormatsOfMedia; 18 | subtitles?: string; 19 | marks?: unknown; 20 | } 21 | 22 | function playMedia( 23 | { name, type, format, subtitles, marks }: MediaProps = { 24 | name: "example", 25 | type: TypesOfMedia.Video, 26 | format: FormatsOfMedia.MP4, 27 | } 28 | ): string { 29 | let marksLog: string; 30 | if (Array.isArray(marks)) { 31 | marksLog = marks.join(" "); 32 | } else if (typeof marks === "string") { 33 | marksLog = marks; 34 | } else { 35 | marksLog = "Unsupported type of marks"; 36 | } 37 | 38 | console.log(`Media ${name}${format} is ${type} 39 | Marks: ${marksLog} 40 | Subtitles: ${subtitles ?? "none"}`); 41 | 42 | return "Media started"; 43 | } 44 | 45 | playMedia({ 46 | name: "WoW", 47 | format: FormatsOfMedia.MOV, 48 | type: TypesOfMedia.Video, 49 | subtitles: "hmhmhm hmhmhm doh", 50 | marks: ["4:30", "5:40"], 51 | }); 52 | -------------------------------------------------------------------------------- /TS_step_3/index.ts: -------------------------------------------------------------------------------- 1 | type ValidAmount = "empty" | number; 2 | 3 | interface ClothesWarehouse { 4 | jackets: ValidAmount; 5 | hats: ValidAmount; 6 | socks: ValidAmount; 7 | pants: ValidAmount; 8 | } 9 | 10 | interface StationeryWarehouse { 11 | scissors: ValidAmount; 12 | paper: "empty" | boolean; 13 | } 14 | 15 | interface AppliancesWarehouse { 16 | dishwashers: ValidAmount; 17 | cookers: ValidAmount; 18 | mixers: ValidAmount; 19 | } 20 | 21 | interface TotalWarehouse 22 | extends ClothesWarehouse, 23 | StationeryWarehouse, 24 | AppliancesWarehouse { 25 | deficit: boolean; 26 | date: Date; 27 | } 28 | 29 | const totalData: TotalWarehouse = { 30 | jackets: 5, 31 | hats: "empty", 32 | socks: "empty", 33 | pants: 15, 34 | scissors: 15, 35 | paper: true, 36 | dishwashers: 3, 37 | cookers: "empty", 38 | mixers: 14, 39 | deficit: true, 40 | date: new Date(), 41 | }; 42 | 43 | function printReport(data: TotalWarehouse): string { 44 | const result: string = Object.entries(data) 45 | .filter((item) => item[1] === "empty") 46 | .reduce((res, item) => `${res} ${item[0]},`, ""); 47 | 48 | if (result.trim().length) { 49 | return `We need this items:${result.slice(0, -1)}`; 50 | } else { 51 | return "Everything fine"; 52 | } 53 | } 54 | 55 | console.log(printReport(totalData)); 56 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Журнал измененений в курсе TypeScript 2 | 3 | В этом файле отображены основные изменения в курсе с датой. По ним вы можете отслеживать любые нововведения и когда они произошли. План будущих обновлений расположен ниже. 4 | 5 | ## Журнал измененений: 6 | 7 | - **30.04.2023** - Добавлены уроки по настройке конфигурации 8 | 9 | --- 10 | 11 | - **21.03.2023** - Добавлены уроки по ES-декораторам из версии Typescript 5.0 12 | 13 | --- 14 | 15 | - **12.03.2023** - Добавлен большой урок по практике декораторов и разбору связанных с этим проблем 16 | 17 | --- 18 | 19 | - **03.03.2023** - Добавлен модуль по декораторам в TS из восьми уроков + статья в начале модуля 20 | 21 | --- 22 | 23 | - **08.02.2023** - Добавлен модуль по использованию классов в TS из 13 уроков, статья в начале модуля и практический тест по всей обязательной информации. В начале курса прикреплен полный конспект по материалу 24 | 25 | --- 26 | 27 | - **17.11.2022** - Курс добавлен на платформу. Сформированы задания, материалы, тесты и тп. 28 | 29 | --- 30 | 31 | ## Будущие обновления: 32 | 33 | - Модуль по настройке TS компилятора и необходимым свойствам в нем 34 | - Модуль по стандартным паттернам в TS (Эти два пункта будут оформлены в одном модуле на платформе вместе с декораторами) 35 | - Использование React вместе с TS 36 | - Практика использования React/TS 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Курс TypeScript (В дальнейшем сюда войдут React+TS) 2 | 3 | В этом репозитории расположены ответы на домашние задания на курсе. Все специально разбито на папки, чтобы вам было удобно смотреть и скачивать. Если вы выполнили задачу или часть проекта - то тут вы можете себя проверить. 4 | В курсе ответы прикреплены в самих уроках с заданиями, либо вы можете найти их здесь по номеру задания. 5 | 6 | Отслеживать изменения в курсе можно [тут](https://github.com/yankovalenko94/TS_task_answers/blob/master/Changelog.md) 7 | 8 | ## Список папок и изменения в них: 9 | 10 | ### TypeScript 11 | 12 | - **TS_Step_1** - Задача на типизацию функций 13 | - **TS_Step_2** - Задача на типизацию объектов и массивов 14 | - **TS_Step_3** - Задача на создание интерфейсов и типов 15 | - **TS_Step_4** - Задача на создание перечислений и различные операторы 16 | - **TS_Step_5** - Задача на создание правильных интерфейсов и typeguard 17 | - **TS_Step_6** - Задача по работе с DOM-структурой и страницей 18 | - **TS_Step_7** - Задача по работе с Generics 19 | - **TS_Step_8** - Задача на работу с манипуляциями с типами 20 | - **TS_Step_9** - Задача на работу с вспомогательными типами и модификациями 21 | - **TS_Step_10** - Задача на работу с имплементацией интерфейсов 22 | - **TS_Step_11** - Задача на работу со структурами данных в классах 23 | - **TS_Step_12** - Задача на работу с декораторами и применением метаданных 24 | -------------------------------------------------------------------------------- /TS_Step_2/index.ts: -------------------------------------------------------------------------------- 1 | const electricityUserData = { 2 | readings: 95, 3 | units: "kWt", 4 | mode: "double", 5 | }; 6 | 7 | const waterUserData = { 8 | readings: 3, 9 | units: "m3", 10 | }; 11 | 12 | const elRate: number = 0.45; 13 | const wRate: number = 2; 14 | 15 | const monthPayments: number[] = [0, 0]; // [electricity, water] 16 | 17 | const calculatePayments = ( 18 | { readings, mode }: { readings: number; mode: string }, 19 | wData: { readings: number }, 20 | elRate: number, 21 | wRate: number 22 | ): void => { 23 | if (mode === "double" && readings < 50) { 24 | monthPayments[0] = readings * elRate * 0.7; 25 | } else { 26 | monthPayments[0] = readings * elRate; 27 | } 28 | 29 | monthPayments[1] = wData.readings * wRate; 30 | }; 31 | 32 | calculatePayments(electricityUserData, waterUserData, elRate, wRate); 33 | 34 | const sendInvoice = ( 35 | [el, water]: number[], 36 | electricityUserData: { readings: number; units: string }, 37 | waterUserData: { readings: number; units: string } 38 | ): string => { 39 | const text = ` Hello! 40 | This month you used ${electricityUserData.readings} ${electricityUserData.units} of electricity 41 | It will cost: ${el}€ 42 | 43 | This month you used ${waterUserData.readings} ${waterUserData.units} of water 44 | It will cost: ${water}€`; 45 | 46 | return text; 47 | }; 48 | 49 | const invoice = sendInvoice(monthPayments, electricityUserData, waterUserData); 50 | console.log(invoice); 51 | -------------------------------------------------------------------------------- /TS_step_8/index.ts: -------------------------------------------------------------------------------- 1 | interface IPhone { 2 | company: string; 3 | number: number; 4 | } 5 | 6 | interface IMobilePhone extends IPhone { 7 | size: string; 8 | companyPartner: IPhone["company"]; 9 | manufactured: Date; 10 | } 11 | 12 | const phones: IMobilePhone[] = [ 13 | { 14 | company: "Nokia", 15 | number: 1285637, 16 | size: "5.5", 17 | companyPartner: "MobileNokia", 18 | manufactured: new Date("2022-09-01"), 19 | }, 20 | { 21 | company: "Samsung", 22 | number: 4356637, 23 | size: "5.0", 24 | companyPartner: "SamMobile", 25 | manufactured: new Date("2021-11-05"), 26 | }, 27 | { 28 | company: "Apple", 29 | number: 4552833, 30 | size: "5.7", 31 | companyPartner: "no data", 32 | manufactured: new Date("2022-05-24T12:00:00"), 33 | }, 34 | ]; 35 | 36 | interface IPhonesManufacturedAfterDate extends IMobilePhone { 37 | initialDate: string; 38 | } 39 | 40 | function filterPhonesByDate( 41 | phones: IMobilePhone[], 42 | key: keyof IMobilePhone, 43 | initial: string 44 | ): IPhonesManufacturedAfterDate[] { 45 | return phones 46 | .filter((phone) => { 47 | const manufactured = phone[key]; 48 | 49 | if ( 50 | manufactured instanceof Date && 51 | manufactured.getTime() > new Date(initial).getTime() 52 | ) { 53 | return phone; 54 | } 55 | }) 56 | .map((phone) => { 57 | const newObj = { ...phone, initialDate: initial }; 58 | return newObj; 59 | }); 60 | } 61 | 62 | console.log(filterPhonesByDate(phones, "manufactured", "2022-01-01")); 63 | -------------------------------------------------------------------------------- /TS_step_5/index.ts: -------------------------------------------------------------------------------- 1 | type Animal = "cat" | "dog" | "bird"; 2 | 3 | // Можно и не создавать перечисление, но вдруг в будущем статусов будет больше? 4 | 5 | enum AnimalStatus { 6 | Available = "available", 7 | NotAvailable = "not available", 8 | } 9 | 10 | interface AnimalData { 11 | animal: Animal; 12 | breed: string; 13 | sterilized?: string; 14 | } 15 | // Не повторяем код, используем extends 16 | interface AnimalAvailableData extends AnimalData { 17 | location: string; 18 | age?: number; 19 | } 20 | 21 | interface AnimalNotAvailableData { 22 | message: string; 23 | nextUpdateIn: Date; 24 | } 25 | 26 | // Интерфейсы стоит разделить, так как оба ответа будут иметь поле data 27 | // И только по статусу будет сложно определить данные 28 | 29 | interface AnimalAvailableResponse { 30 | status: AnimalStatus.Available; 31 | data: AnimalAvailableData; 32 | } 33 | 34 | interface AnimalNotAvailableResponse { 35 | status: AnimalStatus.NotAvailable; 36 | data: AnimalNotAvailableData; 37 | } 38 | 39 | type Res = AnimalAvailableResponse | AnimalNotAvailableResponse; 40 | 41 | function isAvailable(res: Res): res is AnimalAvailableResponse { 42 | if (res.status === AnimalStatus.Available) { 43 | return true; 44 | } else { 45 | return false; 46 | } 47 | } 48 | 49 | function checkAnimalData(animal: Res): AnimalAvailableData | string { 50 | if (isAvailable(animal)) { 51 | return animal.data; 52 | } else { 53 | return `${animal.data.message}, you can try in ${animal.data.nextUpdateIn}`; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /TS_step_6/index.ts: -------------------------------------------------------------------------------- 1 | const forms = document.querySelectorAll("form"); 2 | const email = document.querySelector("#email") as HTMLInputElement; 3 | const title = document.querySelector("#title") as HTMLInputElement; 4 | const text = document.querySelector("#text") as HTMLTextAreaElement; 5 | const checkbox = document.querySelector("#checkbox") as HTMLInputElement; 6 | 7 | interface IFormData { 8 | email: string; 9 | title: string; 10 | text: string; 11 | checkbox: boolean; 12 | } 13 | 14 | const formData: IFormData = { 15 | email: "", 16 | title: "", 17 | text: "", 18 | checkbox: false, 19 | }; 20 | 21 | forms.forEach((form) => 22 | form.addEventListener("submit", (e) => { 23 | e.preventDefault(); 24 | 25 | // Можно и создавать другой объект для соблюдения иммутабельности, но пока не обязательно 26 | formData.email = email?.value ?? ""; 27 | formData.title = title?.value ?? ""; 28 | formData.text = text?.value ?? ""; 29 | formData.checkbox = checkbox?.checked ?? false; 30 | 31 | if (validateFormData(formData)) { 32 | checkFormData(formData); 33 | } 34 | }) 35 | ); 36 | 37 | function validateFormData(data: IFormData): boolean { 38 | // Если каждое из свойств объекта правдиво... 39 | if (Object.values(data).every((value) => value)) { 40 | return true; 41 | } else { 42 | console.log("Please, complete all fields"); 43 | return false; 44 | } 45 | } 46 | 47 | function checkFormData(data: IFormData) { 48 | const { email } = data; 49 | const emails = ["example@gmail.com", "example@ex.com", "admin@gmail.com"]; 50 | 51 | if (emails.some((e) => e === email)) { 52 | console.log("This email is already exist"); 53 | } else { 54 | console.log("Posting data..."); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /TS_step_10/index.ts: -------------------------------------------------------------------------------- 1 | enum TransferStatus { 2 | Pending = "pending", 3 | Rejected = "rejected", 4 | Completed = "completed", 5 | } 6 | 7 | enum ErrorMessages { 8 | NotFound = "Not found: 404", 9 | NotEnoughSpace = "Not enough space: 507", 10 | Forbidden = "Forbidden: 403", 11 | } 12 | 13 | interface ITransfer { 14 | path: string; 15 | data: string[]; 16 | date?: Date; 17 | start: (p: string, d: string[]) => string; 18 | stop: (reason: string) => string; 19 | } 20 | 21 | interface TransferError { 22 | message: ErrorMessages; 23 | } 24 | 25 | class SingleFileTransfer implements ITransfer, TransferError { 26 | path: string; 27 | data: string[]; 28 | date?: Date | undefined; 29 | message: ErrorMessages; 30 | transferStatus: TransferStatus; 31 | 32 | constructor(status: TransferStatus) { 33 | this.transferStatus = status; 34 | } 35 | 36 | start(p: string, d: string[]) { 37 | return 'Transfer started' 38 | } 39 | 40 | // Никто не запрещает создавать стрелочные функции 41 | // С ними проще работать с this 42 | checkTransferStatus = (): string => { 43 | return this.transferStatus; 44 | } 45 | 46 | stop = (reason: string) => { 47 | return `Transfer stopped, reason: ${reason}, Date: ${new Date().toLocaleString()}` 48 | }; 49 | 50 | // Метод может показаться странным, но может использоваться для тестов, например 51 | makeError = (): string => { 52 | // Ведь при ошибке статус всегда "отклонено", правда? 53 | return `Status: ${TransferStatus.Rejected}, error message: ${ErrorMessages.Forbidden}` 54 | } 55 | } 56 | 57 | const transfer = new SingleFileTransfer(TransferStatus.Pending); 58 | console.log(transfer.checkTransferStatus()); 59 | console.log(transfer.stop('Test')); 60 | console.log(transfer.makeError()); -------------------------------------------------------------------------------- /TS_step_6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Form task 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 | 19 |
We'll never share your email with anyone else.
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 | 34 |
35 |
36 | 37 | 38 |
39 | 40 |
41 |
42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /TS_step_9/index.ts: -------------------------------------------------------------------------------- 1 | interface IFitnessClass { 2 | name: string; 3 | startsAt: string; 4 | duration: number; 5 | } 6 | 7 | interface IFutureClass extends Omit { 8 | willStartsAt: string; 9 | } 10 | 11 | interface IClient { 12 | name: string; 13 | age: string | number; 14 | gender: "male" | "female"; 15 | timeLeft: string; 16 | makeCallFor: Date; 17 | } 18 | 19 | type CurrClient = Omit; 20 | type ExClient = Omit; 21 | type FutureClient = Pick; 22 | 23 | interface IFitnessClub { 24 | clubName: string; 25 | location: string; 26 | classes: IFitnessClass[]; 27 | futureClasses: IFutureClass[]; 28 | currClients: CurrClient[]; 29 | exClients: ExClient[]; 30 | futureClients: FutureClient[]; 31 | } 32 | 33 | const fitnessClubCenter: IFitnessClub = { 34 | clubName: "Fitness club Center", 35 | location: "central ave. 45, 5th floor", 36 | classes: [ 37 | { 38 | name: "yoga", 39 | startsAt: "8:00 AM", 40 | duration: 60, 41 | }, 42 | { 43 | name: "trx", 44 | startsAt: "11:00 AM", 45 | duration: 45, 46 | }, 47 | { 48 | name: "swimming", 49 | startsAt: "3:00 PM", 50 | duration: 70, 51 | }, 52 | ], 53 | futureClasses: [ 54 | { 55 | name: "boxing", 56 | willStartsAt: "6:00 PM", 57 | duration: 40, 58 | }, 59 | { 60 | name: "breath training", 61 | willStartsAt: "8:00 PM", 62 | duration: 30, 63 | }, 64 | ], 65 | currClients: [ 66 | { 67 | name: "John Smith", 68 | age: "-", 69 | gender: "male", 70 | timeLeft: "1 month", 71 | }, 72 | { 73 | name: "Alise Smith", 74 | age: 35, 75 | gender: "female", 76 | timeLeft: "3 month", 77 | }, 78 | { 79 | name: "Ann Sonne", 80 | age: 24, 81 | gender: "female", 82 | timeLeft: "5 month", 83 | }, 84 | ], 85 | exClients: [ 86 | { 87 | name: "Tom Smooth", 88 | age: 50, 89 | gender: "male", 90 | makeCallFor: new Date("2023-08-12"), 91 | }, 92 | ], 93 | futureClients: [ 94 | { 95 | name: "Maria", 96 | makeCallFor: new Date("2023-07-10"), 97 | }, 98 | ], 99 | }; 100 | -------------------------------------------------------------------------------- /TS_step_7/index.ts: -------------------------------------------------------------------------------- 1 | interface PlayerData { 2 | game: Game; 3 | hours: Hours; 4 | server: string; 5 | } 6 | 7 | const player1: PlayerData = { 8 | game: "CS:GO", 9 | hours: 300, 10 | server: "basic", 11 | }; 12 | 13 | const player2: PlayerData = { 14 | game: 2048, 15 | hours: "300 h.", 16 | server: "arcade", 17 | }; 18 | 19 | const player3: PlayerData = { 20 | game: "Chess", 21 | hours: { 22 | total: 500, 23 | inMenu: 50, 24 | }, 25 | server: "chess", 26 | }; 27 | 28 | enum FigureNames { 29 | Rect = "rect", 30 | Circle = "circle", 31 | Triangle = "triangle", 32 | Line = "line", 33 | } 34 | 35 | interface Figure { 36 | name: FigureNames; 37 | } 38 | 39 | interface AmountOfFigures { 40 | squares: number; 41 | circles: number; 42 | triangles: number; 43 | others: number; 44 | } 45 | 46 | function calculateAmountOfFigures( 47 | figure: T[] 48 | ): AmountOfFigures { 49 | const amount: AmountOfFigures = { 50 | squares: 0, 51 | circles: 0, 52 | triangles: 0, 53 | others: 0, 54 | }; 55 | 56 | figure.forEach((fig) => { 57 | switch (fig.name) { 58 | case FigureNames.Rect: 59 | amount.squares++; 60 | break; 61 | case FigureNames.Circle: 62 | amount.circles++; 63 | break; 64 | case FigureNames.Triangle: 65 | amount.triangles++; 66 | break; 67 | default: 68 | amount.others++; 69 | } 70 | }); 71 | 72 | return amount; 73 | } 74 | 75 | const data = [ 76 | { 77 | name: FigureNames.Rect, 78 | data: { a: 5, b: 10 }, 79 | }, 80 | { 81 | name: FigureNames.Rect, 82 | data: { a: 6, b: 11 }, 83 | }, 84 | { 85 | name: FigureNames.Triangle, 86 | data: { a: 5, b: 10, c: 14 }, 87 | }, 88 | { 89 | name: FigureNames.Line, 90 | data: { l: 15 }, 91 | }, 92 | { 93 | name: FigureNames.Circle, 94 | data: { r: 10 }, 95 | }, 96 | { 97 | name: FigureNames.Circle, 98 | data: { r: 5 }, 99 | }, 100 | { 101 | name: FigureNames.Rect, 102 | data: { a: 15, b: 7 }, 103 | }, 104 | { 105 | name: FigureNames.Triangle, 106 | }, 107 | ]; 108 | 109 | console.log(calculateAmountOfFigures(data)); 110 | -------------------------------------------------------------------------------- /TS_step_11/index.ts: -------------------------------------------------------------------------------- 1 | interface Queue { 2 | enqueue(item: T): void; // поставить в очередь 3 | dequeue(): T | undefined; // исключить из очереди 4 | peek(): T | undefined | null; // посмотреть первый элемент 5 | isEmpty(): boolean; // проверка на "пустоту" сущности 6 | length(): number; // проверка на длину 7 | } 8 | 9 | // this не обязательно типизировать всегда 10 | // только в тех случаях, когда вы реально можете потерять контекст 11 | // в этом задании я добавил это больше для примера, 12 | // да и вы увидите, что это выглядит довольно излишне 13 | 14 | class ArrayQueue implements Queue { 15 | private queue: T[] = []; 16 | 17 | enqueue(this: ArrayQueue, item: T): void { 18 | this.queue.push(item); 19 | } 20 | 21 | dequeue(this: ArrayQueue): T { 22 | if (this.isEmpty()) { 23 | throw new Error("Queue Underflow"); 24 | } 25 | 26 | return this.queue.shift() as T; 27 | } 28 | 29 | peek(this: ArrayQueue): T | null { 30 | if (this.isEmpty()) { 31 | return null; 32 | } 33 | 34 | return this.queue[0]; 35 | } 36 | 37 | isEmpty(this: ArrayQueue): boolean { 38 | return this.queue.length === 0; 39 | } 40 | 41 | length(this: ArrayQueue): number { 42 | return this.queue.length; 43 | } 44 | } 45 | 46 | class Stack { 47 | private stack: T[] = []; 48 | private limit: number; 49 | 50 | constructor(limit: number = Number.MAX_VALUE) { 51 | this.limit = limit; 52 | } 53 | 54 | push(this: Stack, value: T) { 55 | if (this.length() + 1 > this.limit) { 56 | throw new Error("Stack Overflow"); 57 | } 58 | 59 | this.stack.push(value); 60 | } 61 | 62 | pop(this: Stack): T { 63 | if (this.length() !== 0) { 64 | return this.stack.pop() as T; 65 | } 66 | 67 | throw new Error("Stack Underflow"); 68 | } 69 | 70 | length(this: Stack): number { 71 | return this.stack.length; 72 | } 73 | 74 | isEmpty(this: Stack): boolean { 75 | return this.length() === 0; 76 | } 77 | 78 | top(this: Stack): T | null { 79 | if (this.length() !== 0) { 80 | return this.stack[this.length() - 1]; 81 | } 82 | 83 | return null; 84 | } 85 | } 86 | 87 | // Для тестов 88 | 89 | const arrTest1 = new ArrayQueue(); 90 | arrTest1.enqueue(5); 91 | arrTest1.enqueue(10); 92 | console.log(arrTest1.peek()); 93 | console.log(arrTest1.dequeue()); 94 | console.log(arrTest1.length()); 95 | 96 | const arrTest2 = new ArrayQueue(); 97 | arrTest2.enqueue("5"); 98 | arrTest2.enqueue("10"); 99 | console.log(arrTest2.peek()); 100 | console.log(arrTest2.dequeue()); 101 | console.log(arrTest2.length()); 102 | 103 | const stackTest1 = new Stack(10); 104 | stackTest1.push(20); 105 | stackTest1.push(50); 106 | console.log(stackTest1.top()); 107 | console.log(stackTest1.pop()); 108 | console.log(stackTest1.length()); 109 | 110 | const stackTest2 = new Stack(10); 111 | stackTest2.push("20"); 112 | stackTest2.push("50"); 113 | console.log(stackTest2.top()); 114 | console.log(stackTest2.pop()); 115 | console.log(stackTest2.length()); 116 | -------------------------------------------------------------------------------- /TS_step_12/index.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | 3 | interface ICuboid { 4 | width: number; 5 | length: number; 6 | height: number; 7 | calcArea: (multiply?: number) => number; 8 | calcVolume: (multiply?: number) => number; 9 | } 10 | 11 | @createdAt 12 | class ShippingContainer implements ICuboid { 13 | @IsInt() 14 | @Min(1) 15 | width: number; 16 | 17 | @IsInt() 18 | @Min(1) 19 | length: number; 20 | 21 | @IsInt() 22 | @Min(1) 23 | @Max(8) 24 | height: number; 25 | // lastCalculation: string; 26 | // createdAt: Date; 27 | 28 | constructor(width: number, length: number, height: number) { 29 | this.width = width; 30 | this.length = length; 31 | this.height = height; 32 | validate(this, "width", width); 33 | validate(this, "length", length); 34 | validate(this, "height", height); 35 | } 36 | 37 | @fixLastCalculation("calcArea") 38 | calcArea(multiply?: number): number { 39 | return this.width * this.length * (multiply ? multiply : 1); 40 | } 41 | 42 | @fixLastCalculation("calcVolume") 43 | calcVolume(multiply?: number) { 44 | return this.width * this.length * this.height * (multiply ? multiply : 1); 45 | } 46 | } 47 | 48 | // 1. Необходимо создать декоратор класса, который будет записывать дату создания контейнера 49 | // Простыми словами - создавать в нем новое свойство createdAt с датой создания экземпляра 50 | 51 | // 2. Необходимо создать декораторы IsInt, Min и Max, которые будут валидировать свойства класса 52 | // Применение смотри в самом классе. При ошибке выполняйте throw new Error 53 | // IsInt проверяет на то, что было передано целое число 54 | 55 | // 3. Необходимо создать декоратор метода, который при каждом запуске метода будет создавать 56 | // ИЛИ менять содержимое свойства самого класса lastCalculation 57 | // Как значение записывать в него строку "Последний подсчет ${method} был ${Дата}", 58 | // Где method - это название подсчета, который передается при вызове декоратора (площадь или объем) 59 | 60 | type ShippingContainerData = { 61 | lastCalculation: string; 62 | createdAt: Date; 63 | }; 64 | 65 | const container = new ShippingContainer(10, 100, 7) as ICuboid & 66 | ShippingContainerData; 67 | container.width = 5; 68 | container.height = 5; 69 | console.log(container.createdAt); 70 | console.log(container.calcVolume()); 71 | console.log(container.lastCalculation); 72 | 73 | finalValidate(container); 74 | 75 | function fixLastCalculation(method: string) { 76 | return ( 77 | target: Object, 78 | propertyKey: string | symbol, 79 | descriptor: PropertyDescriptor 80 | ): PropertyDescriptor | void => { 81 | const oldValue = descriptor.value; 82 | descriptor.value = function (this: any, ...args: any[]) { 83 | this.lastCalculation = `Последний подсчет ${method} был ${new Date()}`; 84 | return oldValue.apply(this, args); 85 | }; 86 | }; 87 | } 88 | 89 | function createdAt(constructor: T) { 90 | return class extends constructor { 91 | createdAt = new Date(); 92 | }; 93 | } 94 | 95 | function IsInt() { 96 | return function (target: any, propertyKey: string) { 97 | Reflect.defineMetadata("IsInt", true, target, propertyKey); 98 | }; 99 | } 100 | 101 | function Min(value: number) { 102 | return function (target: any, propertyKey: string) { 103 | Reflect.defineMetadata("Min", value, target, propertyKey); 104 | }; 105 | } 106 | 107 | function Max(value: number) { 108 | return function (target: any, propertyKey: string) { 109 | Reflect.defineMetadata("Max", value, target, propertyKey); 110 | }; 111 | } 112 | 113 | function finalValidate(obj: unknown) { 114 | if (obj && typeof obj === "object" && !Array.isArray(obj)) { 115 | for (let key in obj) { 116 | validate(obj, key, obj[key as keyof typeof obj]); 117 | } 118 | } 119 | } 120 | 121 | function validate(target: Object, propertyKey: string, value: any) { 122 | if ( 123 | Reflect.getMetadata("IsInt", target, propertyKey) && 124 | (!Number.isInteger(value) || value !== parseInt(value)) 125 | ) { 126 | throw new Error(`property ${propertyKey} should be an integer`); 127 | } 128 | if ( 129 | Reflect.hasMetadata("Min", target, propertyKey) && 130 | value < Reflect.getMetadata("Min", target, propertyKey) 131 | ) { 132 | throw new Error( 133 | `min value for ${propertyKey} is ${Reflect.getMetadata( 134 | "Min", 135 | target, 136 | propertyKey 137 | )}` 138 | ); 139 | } 140 | if ( 141 | Reflect.hasMetadata("Max", target, propertyKey) && 142 | value > Reflect.getMetadata("Max", target, propertyKey) 143 | ) { 144 | throw new Error( 145 | `max value for ${propertyKey} is ${Reflect.getMetadata( 146 | "Max", 147 | target, 148 | propertyKey 149 | )}` 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /TS_step_6/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "resolveJsonModule": true, /* Enable importing .json files. */ 39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 40 | 41 | /* JavaScript Support */ 42 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | 46 | /* Emit */ 47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 52 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 53 | // "removeComments": true, /* Disable emitting comments. */ 54 | // "noEmit": true, /* Disable emitting files from a compilation. */ 55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | 71 | /* Interop Constraints */ 72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 77 | 78 | /* Type Checking */ 79 | "strict": true, /* Enable all strict type-checking options. */ 80 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 98 | 99 | /* Completeness */ 100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 102 | } 103 | } 104 | --------------------------------------------------------------------------------