diff --git a/nginx.conf b/nginx.conf index 3d8217ef80..2fc73d1bd4 100644 --- a/nginx.conf +++ b/nginx.conf @@ -308,6 +308,8 @@ server { # Removed pages # GPT plugins were discontinued April 9th, 2024 - https://help.openai.com/en/articles/8988022-winding-down-the-chatgpt-plugins-beta rewrite ^/platform/integrations/chatgpt-plugin$ https://blog.apify.com/add-custom-actions-to-your-gpts/ redirect; + # TypeScript course was removed from the Academy - https://github.com/apify/apify-docs/pull/1552 + rewrite ^/academy/switching-to-typescript(/.*)?$ https://apify.com/templates/categories/typescript permanent; # Python docs diff --git a/sources/academy/webscraping/typescript/enums.md b/sources/academy/webscraping/typescript/enums.md deleted file mode 100644 index 4927d4f68b..0000000000 --- a/sources/academy/webscraping/typescript/enums.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: Enums -description: Learn how to define, use, and manage constant values using a cool feature called "enums" that TypeScript brings to the table. -sidebar_position: 7.4 -slug: /switching-to-typescript/enums ---- - -# Enums! {#enums} - -**Learn how to define, use, and manage constant values using a cool feature called "enums" that TypeScript brings to the table.** - ---- - -Enums are a nice feature offered by TypeScript that can be used to create automatically enumerated global constant identifiers that can also be used as custom types. We've dedicated an entire lesson to enums because they're a new feature brought into JavaScript by TypeScript, and because they can be quite useful in certain projects. - -## Let's talk about constants {#lets-talk-about-constants} - -If you've followed along with any of the more advanced courses in the Apify academy, or at least read the [Best practices](../scraping_basics_javascript/best_practices.md) lesson in the **Web scraping basics for JavaScript devs** course, you'll definitely be familiar with the idea of constant variables. In a nutshell, we create constant variables for values that will never change, and will likely used in multiple places. The naming convention for constants is **ALL_CAPS_AND_UNDERSCORED**. - -Here's an object of constant values that we've prepared for use within our project. - -```ts -const fileExtensions = { - JAVASCRIPT: '.js', - TYPESCRIPT: '.ts', - RUST: '.rs', - PYTHON: '.py', -}; -``` - -No problem, this will totally work; however, the issue is that TypeScript doesn't know what these values are - it infers them to just be strings. We can solve this by adding a type annotation with a custom type definition: - -```ts -// Since TypeScript infers these values to be just strings, -// we have to create a type definition telling it that these -// properties hold super specific strings. -const fileExtensions: { - JAVASCRIPT: '.js'; - TYPESCRIPT: '.ts'; - RUST: '.rs'; - PYTHON: '.py'; -// Define the object's values -} = { - JAVASCRIPT: '.js', - TYPESCRIPT: '.ts', - RUST: '.rs', - PYTHON: '.py', -}; - -// Or, we can just do this -const fileExtensions = { - JAVASCRIPT: '.js', - TYPESCRIPT: '.ts', - RUST: '.rs', - PYTHON: '.py', -} as const; -``` - -> Using an actual concrete value such as `'.js'` or `24` or something else instead of a type name is called a [literal type](https://www.typescriptlang.org/docs/handbook/literal-types.html). - -And now we'll create a variable with a hacky custom type that points to the values in the `fileExtensions` object: - -![TypeScript autofilling the values of the fileExtensions object](./images/constant-autofill.png) - -Because of the custom type definition for `fileExtensions` and the type annotation used for the `values` variable, we are getting some autofill for the variable, and the variable can only be set to values within the `fileExtensions` object. Though this implementation works, TypeScript offers a unique new feature called **enums** that was designed just for these use cases. - -## Creating enums {#creating-enums} - -The [`enum`](https://www.typescriptlang.org/docs/handbook/enums.html) keyword is a new keyword brought to us by TypeScript that allows us the same functionality we implemented in the above section, plus more. To create one, use the keyword followed by the name you'd like to use (the naming convention is generally **CapitalizeEachFirstLetterAndSingular**). - -```ts -enum FileExtension { - // Use an "=" sign instead of a ":" - JAVASCRIPT = '.js', - TYPESCRIPT = '.ts', - RUST = '.rs', - PYTHON = '.py', -} -``` - -## Using enums {#using-enums} - -Using enums is straightforward. Use dot notation as you would with a regular object. - -```ts -enum FileExtension { - JAVASCRIPT = '.js', - TYPESCRIPT = '.ts', - RUST = '.rs', - PYTHON = '.py', -} - -const value = FileExtension.JAVASCRIPT; - -console.log(value); // => ".js" -``` - -## Using enums as types {#using-enums-as-types} - -The nice thing about enums is that they can be used directly in type annotations as somewhat of a custom type. Observe this function: - -```ts -const createFileName = (name: string, extension: string) => { - return name + extension; -}; -``` - -We can restrict `extension` so that it can only be a value in the enum by replacing `extension: string` with `extension: FileExtension`: - -```ts -enum FileExtension { - JAVASCRIPT = '.js', - TYPESCRIPT = '.ts', - RUST = '.rs', - PYTHON = '.py', -} - -const createFileName = (name: string, extension: FileExtension) => { - return name + extension; -}; - -// Call the function and use the enum to populate the second parameter -const fileName = createFileName('hello', FileExtension.TYPESCRIPT); - -console.log(fileName); -``` - -We don't get autocomplete, but the `extension` parameter is now restricted to the values defined in the `FileExtension` enum. - -## Next up {#next} - -The `enum` keyword is just the tip of the iceberg of the exclusive features TypeScript has to offer. Let's now [learn about](./type_aliases.md) type aliases (custom types!) with the `type` keyword. diff --git a/sources/academy/webscraping/typescript/images/another-error.png b/sources/academy/webscraping/typescript/images/another-error.png deleted file mode 100644 index 769d15e8be..0000000000 Binary files a/sources/academy/webscraping/typescript/images/another-error.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/constant-autofill.png b/sources/academy/webscraping/typescript/images/constant-autofill.png deleted file mode 100644 index d7847a8a9d..0000000000 Binary files a/sources/academy/webscraping/typescript/images/constant-autofill.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/epic-autocomplete.png b/sources/academy/webscraping/typescript/images/epic-autocomplete.png deleted file mode 100644 index 663875857e..0000000000 Binary files a/sources/academy/webscraping/typescript/images/epic-autocomplete.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/is-any.png b/sources/academy/webscraping/typescript/images/is-any.png deleted file mode 100644 index 27c01817a4..0000000000 Binary files a/sources/academy/webscraping/typescript/images/is-any.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/no-more-error.png b/sources/academy/webscraping/typescript/images/no-more-error.png deleted file mode 100644 index 38c23c15cf..0000000000 Binary files a/sources/academy/webscraping/typescript/images/no-more-error.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/number-inference.png b/sources/academy/webscraping/typescript/images/number-inference.png deleted file mode 100644 index 72ce5e0de6..0000000000 Binary files a/sources/academy/webscraping/typescript/images/number-inference.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/object-inference.png b/sources/academy/webscraping/typescript/images/object-inference.png deleted file mode 100644 index c8bb302c5b..0000000000 Binary files a/sources/academy/webscraping/typescript/images/object-inference.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/object-type-error.png b/sources/academy/webscraping/typescript/images/object-type-error.png deleted file mode 100644 index 605dc58300..0000000000 Binary files a/sources/academy/webscraping/typescript/images/object-type-error.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/parameter-type.png b/sources/academy/webscraping/typescript/images/parameter-type.png deleted file mode 100644 index 02ff18a675..0000000000 Binary files a/sources/academy/webscraping/typescript/images/parameter-type.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/pasted-example.png b/sources/academy/webscraping/typescript/images/pasted-example.png deleted file mode 100644 index 3f4849250b..0000000000 Binary files a/sources/academy/webscraping/typescript/images/pasted-example.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/promise-any.png b/sources/academy/webscraping/typescript/images/promise-any.png deleted file mode 100644 index c15ea08a81..0000000000 Binary files a/sources/academy/webscraping/typescript/images/promise-any.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/replace-with-unknown.png b/sources/academy/webscraping/typescript/images/replace-with-unknown.png deleted file mode 100644 index b7ad033144..0000000000 Binary files a/sources/academy/webscraping/typescript/images/replace-with-unknown.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/return-inferred.png b/sources/academy/webscraping/typescript/images/return-inferred.png deleted file mode 100644 index 90232d73a5..0000000000 Binary files a/sources/academy/webscraping/typescript/images/return-inferred.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/string-not-number.png b/sources/academy/webscraping/typescript/images/string-not-number.png deleted file mode 100644 index d9eea03cea..0000000000 Binary files a/sources/academy/webscraping/typescript/images/string-not-number.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/typescript-error.png b/sources/academy/webscraping/typescript/images/typescript-error.png deleted file mode 100644 index bcaccb3257..0000000000 Binary files a/sources/academy/webscraping/typescript/images/typescript-error.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/typescript-logo.webp b/sources/academy/webscraping/typescript/images/typescript-logo.webp deleted file mode 100644 index 8254e711dd..0000000000 Binary files a/sources/academy/webscraping/typescript/images/typescript-logo.webp and /dev/null differ diff --git a/sources/academy/webscraping/typescript/images/we-need-overloads.png b/sources/academy/webscraping/typescript/images/we-need-overloads.png deleted file mode 100644 index 84ef95cfe8..0000000000 Binary files a/sources/academy/webscraping/typescript/images/we-need-overloads.png and /dev/null differ diff --git a/sources/academy/webscraping/typescript/index.md b/sources/academy/webscraping/typescript/index.md deleted file mode 100644 index 2d35875989..0000000000 --- a/sources/academy/webscraping/typescript/index.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Switching to TypeScript -description: In this course, learn what TypeScript is, why you should use it instead of vanilla JavaScript, and how to start using it in your own projects. -sidebar_position: 5 -category: web scraping & automation -slug: /switching-to-typescript ---- - -# Switching to TypeScript {#switching-to-typescript} - -> In this course, learn what TypeScript is, why you should use it instead of vanilla JavaScript, and how to start using it in your own projects. - -As the world of JavaScript moves forward, [TypeScript](https://www.typescriptlang.org/) continues to become more popular. More companies than ever before are using TypeScript in their codebases, and are heavily preferring it over vanilla JavaScript. But why? What is TypeScript, and why it is so great for developers? - -![TypeScript logo](./images/typescript-logo.webp) - -## What is TypeScript? {#what-is-typescript} - -If you're familiar with the fundamentals of any programming language (JavaScript included), then you're familiar with the concept of **types**. String, boolean, array, object, number - these are all types. What TypeScript does is bring [type safety](https://en.wikipedia.org/wiki/Type_safety) to JavaScript, which is normally a [dynamically typed](https://developer.mozilla.org/en-US/docs/Glossary/Dynamic_typing) and [interpreted](https://www.geeksforgeeks.org/difference-between-compiled-and-interpreted-language/) programming language. This means that if you have declared a variable like this: `const foo = 'bar'`, then later try to access the non-existent property of `foo.baz`, you'll only know about the error once it happens during runtime. - -To sum everything said up above, here's a code example written in JavaScript that has a couple of problems with it: - -```js -const john = { - name: 'john', - job: 'web developer', -}; - -const bob = { - name: 'bob', - job: 'data analyst', - age: '27', -}; - -const addAges = (num1, num2) => num1 + num2; - -console.log(addAges(bob.age, john.age)); -``` - -This code doesn't actually throw an error, but it does output `27undefined`. That's not good. The first issue is that `john.age` is **undefined**, and the second issue is that `bob.age` is a string and must be converted to a number to work properly in the `addAges` function. Despite these two significant mistakes, JavaScript doesn't tell us at all about them and lets the code run with bugs. - -With TypeScript, these types of issues stick out like a sore thumb, and depending on your configurations, the [compiler](https://www.techtarget.com/whatis/definition/compiler#:~:text=A%20compiler%20is%20a%20special,as%20Java%20or%20C%2B%2B.) will refuse to compile it until they have been fixed. - -![TS Error 2345](./images/typescript-error.png) - -This means that when using TS (a popular acronym for "TypeScript") on a large project, you'll run into much fewer runtime errors and catch the majority of them during the development process. - -## What are the advantages of using TypeScript? {#advantages-of-typescript} - -1. The ability to **optionally** [statically type](https://developer.mozilla.org/en-US/docs/Glossary/Static_typing) your variables and functions. -2. [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html), which provides you the benefits of using types, but without having to actually statically type anything. For example, if you create a variable like this: `let num = 5`, TypeScript will automatically infer that `num` is of a **number** type. -3. Access to the newest features in JavaScript before they are officially supported everywhere. -4. Fantastic support with [IntelliSense](https://en.wikipedia.org/wiki/Intelligent_code_completion) and epic autocomplete when writing functions, accessing object properties, etc. Most IDEs have TypeScript support. -5. Access to exclusive TypeScript features such as [Enums](https://www.typescriptlang.org/docs/handbook/enums.html). - - -## How different is TypeScript from JavaScript? {#how-different-is-it} - -Think of it this way: JavaScript **IS** TypeScript, but TypeScript isn't JavaScript. All JavaScript code is valid TypeScript code, which means that you can pretty much turn any **.js** file into a **.ts** file and it'll still work just the same after being compiled. It also means that to learn TypeScript, you aren't going to have to learn a whole new programming language if you already know JavaScript. - -What are the differences? Well, there's really just one: TypeScript files cannot be run directly. They must first be compiled into regular JavaScript. - -## Ready to get started? {#first} - -Now that you're familiar with what TypeScript is and aware of its many advantages, let's [get started](./installation.md) in our TS journey by installing the TypeScript compiler (super easy) and writing our first line of code in a **.ts** file. diff --git a/sources/academy/webscraping/typescript/installation.md b/sources/academy/webscraping/typescript/installation.md deleted file mode 100644 index 330cd794d3..0000000000 --- a/sources/academy/webscraping/typescript/installation.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Installation & getting started -description: Install TypeScript and the TS compiler on your machine, then write your very first lines of TypeScript code by fixing a logical bug in a vanilla JS snippet. -sidebar_position: 7.1 -slug: /switching-to-typescript/installation ---- - -# Installation & getting started {#installation-getting-started} - -**Install TypeScript and the TS compiler on your machine, then write your very first lines of TypeScript code by fixing a logical bug in a vanilla JS snippet.** - ---- - -> In order to install and use TypeScript, you'll first need to make sure you've installed [Node.js](https://nodejs.org). Node.js comes with a package manager called [npm](https://npmjs.com), through which TypeScript can be installed. - -To install TypeScript globally on your machine, run the following command in your terminal: - -```shell -npm install -g typescript -``` - -> On MacOS or Linux, you might need to prefix this command with `sudo`. - -That's it! - -## Understanding the problems TypeScript solves {#understanding-what-typescript-solves} - -To aid in properly showing some of the benefits TypeScript brings, here is some vanilla JavaScript code that has a bug. This bug will not throw an error or cause the program to crash, but it is a logical error that does not output what we expect. - -```js -const products = [ - { - title: 'iPhone', - price: '1000', - }, - { - title: 'iPad', - price: '1099', - }, -]; - -const addPrices = (price1, price2) => { - return price1 + price2; -}; - -console.log(addPrices(products[0].price, products[1].price)); -``` - -The output of this code is **10001099**, because instead of adding two numbers together, we've concatenated two strings together using the `+` operator. Surely, this is not what we wanted to do. - -No problem, right? We can just add a type check within the `addPrices` function so that it can support both strings and numbers: - -```js -const products = [ - { - title: 'iPhone', - price: '1000', - }, - { - title: 'iPad', - price: '1099', - }, -]; - -const addPrices = (price1, price2) => { - // If they are numbers just add them together - if (typeof price1 === 'number' && typeof price2 === 'number') { - return price1 + price2; - } - - // Otherwise, convert them to numbers and add them together - return +price1 + +price2; -}; - -console.log(addPrices(products[0].price, products[1].price)); -``` - -Now, the output of our code is **2099**, exactly what we wanted. However, this extra logic hasn't really solved our issue. We only ever want numbers to be passed into `addPrices`, and nothing else. This is where TS comes in handy. - -## Writing your first TypeScript code {#writing-your-first-typescript-code} - -> We recommend using [VSCode](https://code.visualstudio.com/) to write your TypeScript code, but feel free to use any IDE that supports TypeScript when following along with this course. - -Let's create a folder called **learning-typescript**, adding a new file within it named **first-lines.ts**. Then, we'll go ahead and paste the very first code example from this lesson into that file. Once again, the example is written in vanilla JavaScript, but since all JavaScript code is valid TypeScript code, this will be pretty much seamless. - -![Example pasted into first-lines.ts](./images/pasted-example.png) - -As seen above, TypeScript has successfully recognized our code; however, there are now red underlines under the `price1` and `price2` parameters in the function declaration of `addPrices`. This is because right now, the compiler has no idea what data types we're expecting to be passed in. This can be solved with the addition of **type annotations** to the parameters by using a colon (`:`) and the name of the parameter's type. - -```ts -const products = [ - { - title: 'iPhone', - price: '1000', - }, - { - title: 'iPad', - price: '1099', - }, -]; - -// Add type annotations so that now the function will -// only accept numbers. -const addPrices = (price1: number, price2: number) => { - return price1 + price2; -}; - -console.log(addPrices(products[0].price, products[1].price)); -``` - -Since the function now only accepts numbers, the parameters in the function call within the `console.log` at the bottom of the file are now underlined in red. - -![Example pasted into first-lines.ts](./images/another-error.png) - -This is because TypeScript has automatically inferred (without us even needing to do anything) that `products` is an array of objects containing `title` and `price` properties - both strings. Because of this type inference, it knows that `products[0].price` and `products[1].price` are both strings, and does not allow them to be passed into `addPrices`, which only accepts numbers. We'll solve this by converting the values to numbers when passing them into the function. - -```ts -const products = [ - { - title: 'iPhone', - price: '1000', - }, - { - title: 'iPad', - price: '1099', - }, -]; - -const addPrices = (price1: number, price2: number) => { - return price1 + price2; -}; - -// Convert the values to numbers as they are passed in -console.log(addPrices(+products[0].price, +products[1].price)); -``` - -Now, our code will work exactly as it should, and we can feel certain that no similar mistakes will be made when using the `addPrices` function again. - -## Compiling {#compiling} - -In order to run the code we wrote in the above section, we'll have to compile the **first-lines.ts** file into vanilla JavasScript. - -Using the TypeScript compiler is extremely straightforward. Just run the `tsc` command in your terminal with the path to the file to compile, and the compiler will do its magic! - -```shell -tsc first-lines.ts -``` - -A **first-lines.js** file appears after running this command, which can be run like normal with this command: - -```shell -node first-lines.js -``` - -Outputted is `2099`, exactly what we expected. Awesome! - -## Next up {#next} - -In the [next lesson](./using_types.md), we'll be discussing how to use some of the core types that TypeScript offers. diff --git a/sources/academy/webscraping/typescript/interfaces.md b/sources/academy/webscraping/typescript/interfaces.md deleted file mode 100644 index b83cd96705..0000000000 --- a/sources/academy/webscraping/typescript/interfaces.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Interfaces -description: Understand how to declare object types with the "interface" keyword, and the subtle difference interfaces have with regular type aliases. -sidebar_position: 7.8 -slug: /switching-to-typescript/interfaces ---- - -# Interfaces {#interfaces} - -**Understand how to declare object types with the "interface" keyword, and the subtle difference interfaces have with regular type aliases.** - ---- - -In the [**Using types - II**](./using_types_continued.md) lesson, you learned about object types and how to create them. Let's create a new custom object type using the `type` keyword that we're already familiar with. - -```ts -type Person = { - name: string; - age: number; - hobbies: string[]; -}; -``` - -We can keep this just as it is, which would be totally okay, or we could use an [interface](https://www.typescriptlang.org/docs/handbook/typescript-tooling-in-5-minutes.html#interfaces). Interfaces and type aliases are nearly identical in their functionality; however there are two main differences: - -1. Interfaces can **ONLY** hold function or object types. -2. Types can only be declared once, while interfaces of the same name can be declared multiple times and will automatically merge. -3. The syntax between the two differs slightly. - -> When working with object types, it usually just comes down to preference whether you decide to use an interface or a type alias. - -Using the `interface` keyword, we can turn our `Person` type into an interface. - -```ts -// Interfaces don't need an "=" sign -interface Person { - name: string; - age: number; - hobbies: string[]; -} -``` - -When creating a new interface, you can also use the `extends` keyword to inherit all of the object properties from another interface (or type alias): - -```ts -interface Person { - name: string; - age: number; - hobbies: string[]; -} - -// This would also work just fine if "Person" -// was declared with the "type" keyword instead -interface Employee extends Person { - occupation: string; -} -``` - -This is functionality is not unique to interfaces though, as it can be done with something called an [intersection type](https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types) when using the `type` keyword. - -```ts -type Employee = Person & { - occupation: string; -}; -``` - -Overall, the differences between interfaces and type aliases are quite subtle. In some use cases, one might be better than the other, but in general it's up to you which you want to use. - -> To learn more about the differences between `interface` and `type`, check out [this article](https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c). - -## Next up {#next} - -It's finally time to [build our project](./mini_project.md)! The project we'll be building in the next lesson will be a small API scraper utilizes many of the TypeScript features learned in the course. diff --git a/sources/academy/webscraping/typescript/mini_project.md b/sources/academy/webscraping/typescript/mini_project.md deleted file mode 100644 index 1bfd7e7b6b..0000000000 --- a/sources/academy/webscraping/typescript/mini_project.md +++ /dev/null @@ -1,465 +0,0 @@ ---- -title: Mini-project -description: Build an entire project in TypeScript using concepts learned in this course. Also, learn about two more advanced TypeScript features. -sidebar_position: 7.9 -slug: /switching-to-typescript/mini-project ---- - -# Mini-project {#mini-project} - -**Build an entire project in TypeScript using concepts learned in this course. Also, learn about two more advanced TypeScript features.** - ---- - -You're here! If you made it this far, that means that you've familiarized yourself with all the core concepts in TypeScript (and some of the more advanced ones too). We're going to scrape product data from [this API endpoint](https://dummyjson.com/products), and then manipulate it based on user input. - -## Project overview {#project-overview} - -Here's a rundown of what our project should be able to do: - -1. Accept an object with two properties as its input. The **sort** property defines how to sort the data, and can be either **ascending** or **descending**. **removeImages** determines whether or not the images array should be removed from each product. -2. Fetch the data and get full TypeScript support on the response object (no `any`!). -3. Sort and modify the data, receiving TypeScript support for the new modified data. - -We'll be using a single external package called [**Axios**](https://www.npmjs.com/package/axios) to fetch the data from the API, which can be installed with the following command: - -```shell -npm i axios -``` - -## Writing types {#writing-types} - -Especially when writing a scraper, it's extremely helpful to write out your data types before even writing a single line of logic. When you understand the data types your program is going to be working with, it's much easier to think how **how** it will work with them. - -### Defining object types for API responses {#defining-object-types-for-api-responses} - -Our Dummy JSON API returns an array of products that look like this: - -```json -{ - "id": 1, - "title": "iPhone 9", - "description": "An apple mobile which is nothing like apple", - "price": 549, - "discountPercentage": 12.96, - "rating": 4.69, - "stock": 94, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://dummyjson.com/image/i/products/1/thumbnail.jpg", - "images": [ - "https://dummyjson.com/image/i/products/1/1.jpg", - "https://dummyjson.com/image/i/products/1/2.jpg", - "https://dummyjson.com/image/i/products/1/3.jpg", - "https://dummyjson.com/image/i/products/1/4.jpg", - "https://dummyjson.com/image/i/products/1/thumbnail.jpg" - ] -} -``` - -Let's write an object type for a product in a new file called **types.ts**: - -```ts -// types.ts -export interface Product { - id: number; - title: string; - description: string; - price: number; - discountPercentage: number; - rating: number; - stock: number; - brand: string; - category: string; - thumbnail: string; - images: string[]; -} -``` - -Great! In the response, the array of products is stored under a key named **products**, so we'll create a `ResponseData` type to represent this as well: - -```ts -// types.ts - -// ... -export interface ResponseData { - products: Product[]; -} -``` - -> Notice that we are `export`ing these so that they can be used back in **index.ts** later on. - -### Defining output types {#defining-output-types} - -Luckily for us, we'll be outputting an array of `Product`s, for which we've already written a type. However, the user will have an option to modify each product by removing the **images** property from each one. We need to create a new `ModifiedProduct` that has all the same properties as `Product` **EXCEPT** for **images**. - -For this, we can use a [utility type](https://www.typescriptlang.org/docs/handbook/utility-types.html) called `Omit`, which is natively available in TypeScript. - -```ts -// types.ts - -// ... -export type ModifiedProduct = Omit; -``` - -This type takes in some arguments (a [generic](https://www.typescriptlang.org/docs/handbook/2/generics.html), as they are called in TS), notated with `<>`. The first one is the type to remove the property from, in our case it's `Product`, and the second is the property to remove. - -### Defining input types {#defining-input-types} - -The user of our scraper will have to provide some input. First, we'll use an enum to define the types of sorting we want to support: - -```ts -// types.ts - -// ... -export enum SortOrder { - ASC = 'ascending', - DESC = 'descending', -} -``` - -And finally, we'll create a `UserInput` type which takes in an argument (a generic type). - -```ts -// types.ts - -// ... -export interface UserInput { - sort: 'ascending' | 'descending'; - removeImages: RemoveImages; -} -``` - -But hold on a minute, we didn't even learn about generics in this course! - -#### Quick chat about generics {#generics} - -"Generics" is just a fancy term for arguments that can be passed into a type. Just like regular JavaScript function arguments, they can be passed in and anything can be done with them. Let's break it down: - -```ts -// We can give "RemoveImages" any name, as it is just representative of an argument that will be passed into. - -// "RemoveImages" can't be just any type. It must EXTEND a boolean, meaning it must be either true or false. - -// By using the "=" sign, we make "RemoveImages" an optional type argument. It will default to the "boolean" type. -export interface UserInput { - sort: 'ascending' | 'descending'; - // The type passed in is being set as the type for the "removeImages" property - removeImages: RemoveImages; -} -``` - -Using this generic allows us to go a step further in how specific we are being by adding the ability to literally specify whether or not **removeImages** is `true` or `false`. A bit later, you'll see why we want this functionality. - -> We recommend reading up on generics [in the TypeScript documentation](https://www.typescriptlang.org/docs/handbook/2/generics.html) to fully understand this slightly more advanced concept. - -### Final types.ts file {#final-types} - -```ts -// types.ts -export interface Product { - id: number; - title: string; - description: string; - price: number; - discountPercentage: number; - rating: number; - stock: number; - brand: string; - category: string; - thumbnail: string; - images: string[]; -} - -export interface ResponseData { - products: Product[]; -} - -export type ModifiedProduct = Omit; - -// Usually, you'd have this in a file holding constants -export enum SortOrder { - ASC = 'ascending', - DESC = 'descending', -} - -export interface UserInput { - sort: 'ascending' | 'descending'; - removeImages: RemoveImages; -} -``` - -## Fetching the data {#fetching-the-data} - -First, let's go ahead and import **axios** and write our fetching function. - -```ts -// index.ts -import axios from 'axios'; - -const fetchData = async () => { - const { data } = await axios('https://dummyjson.com/products?limit=100'); - - return data; -}; -``` - -Easy enough, right? Well, not really. Let's take a look at how TypeScript interprets the function by hovering over it. - -![Promise of any type](./images/promise-any.png) - -We're returning a promise of any type out of this function. This is where we can use [type assertions](./unknown_and_type_assertions.md) to help TypeScript understand that this response takes the shape of our `ResponseData` type. - -```ts -// index.ts -import axios from 'axios'; - -// You don't need to add the word "type" after the "import" keyword when -// importing types, but it can help improve code readability and also prevent -// classes from being used normally if they're imported as just a type. -import type { ResponseData } from './types'; - -const fetchData = async () => { - const { data } = await axios('https://dummyjson.com/products?limit=100'); - - return data as ResponseData; -}; -``` - -Now, the return type is `Promise` - much better! Because of this small change, we'll receive full TypeScript support (and IDE autocomplete) on the return value of `fetchData`. - -## Sorting the data {#sorting-the-data} - -Now, we'll write a function that will sort an array of products. - -```ts -// index.ts - -// ... - -// Take in an array of products, as well as the order in which they should be sorted -const sortData = (products: Product[], order: SortOrder) => { - // Logic will go here -}; -``` - -Since we `SortOrder` ahead of time, we know exactly which cases we need to handle. This is just one example of how writing key important types and constants prior to writing any code can be beneficial. - -```ts -// index.ts -// ... -import { SortOrder } from './types'; - -import type { ResponseData, Product } from './types'; -// ... -const sortData = (products: Product[], order: SortOrder) => { - switch (order) { - // Handle ascending and descending sorting - case SortOrder.ASC: - return [...products].sort((a, b) => a.price - b.price); - case SortOrder.DESC: - return [...products].sort((a, b) => b.price - a.price); - // If for whatever reason the value provided isn't in our SortOrder - // enum, just return the products as they were - default: - return products; - } -}; -``` - -## Putting the pieces together {#putting-the-pieces-together} - -Because of the abstractions we've made with the `fetchData` and `sortData` functions, we can now write another small function called `scrape` which will do the following: - -1. Take in an object matching the `UserInput` type. -2. Fetch the data with the `fetchData` function. -3. Sort it with the `sortData` function. -4. Remove the images from each product (if requested by the user). - -```ts -// index.ts -// ... -import { SortOrder } from './types'; - -import type { ResponseData, Product, UserInput, ModifiedProduct } from './types'; -// ... - -// Return a promise of either a "Product" array, or a "ModifiedProduct" array -async function scrape(input: UserInput): Promise { - // Fetch the data - const data = await fetchData(); - - // Sort the products based on the input's "sort" property. We have - // to cast it to "SortOrder" because despite being equal, technically - // the string "ascending" isn't the same type as SortOrder.ASC - const sorted = sortData(data.products, input.sort as SortOrder); - - // If the user wants to remove images, map through each product removing - // the images and return the result - if (input.removeImages) { - return sorted.map((item) => { - const { images, ...rest } = item; - - return rest; - }); - } - - // Otherwise, just return the sorted products - return sorted; -} -``` - -## Running the scraper {#running-the-scraper} - -Finally, we'll create a new function called `main` which will initialize the input, call the `scrape` function, and return the result. - -```ts -// index.ts - -// ... -const main = async () => { - const INPUT: UserInput = { sort: 'ascending', removeImages: true }; - - const result = await scrape(INPUT); - - console.log(result); -}; -``` - -And that's it! Well, not quite. We are unable to access the **images** property on `result[0]`, which makes total sense. However, even if we switch **removeImages** in the `INPUT` variable to `false`, we still get an error when trying to access that property, even though we know that it hasn't been filtered out. - -![Can't access the "images" property even though it exists](./images/we-need-overloads.png) - -This is because we haven't been specific enough. The `scrape` function can return two different data types (either `Product[]` or `ModifiedProduct[]`), and because `ModifiedProduct` doesn't have an **images** property, TypeScript freaks out and says that you shouldn't be trying to access it. This can be fixed with **overloads**. - -## Let's talk about overloads {#lets-talk-about-overloads} - -[Overloads](https://www.tutorialsteacher.com/typescript/function-overloading) come in handy when you've written a function that returns different things based on what types of arguments were passed into it. - -We need to tell TypeScript that the `scrape` function returns a `Product[]` when the input has a type of `UserInput`, but a `ModifiedProduct[]` when the input has a type of `UserInput`. This can be done by declaring the function multiple types with different parameter types and specifying the return type for each one. - -```ts -// index.ts - -// ... -// If "removeImages" is true, a ModifiedProduct array will be returned -async function scrape(input: UserInput): Promise; -// If false, a normal product array is returned -async function scrape(input: UserInput): Promise; -// The main function declaration, which accepts all types in the declarations above. -// Notice that it has no explicit return type, since they are defined in the -// overloads above. -async function scrape(input: UserInput) { - const data = await fetchData(); - - const sorted = sortData(data.products, input.sort as SortOrder); - - if (input.removeImages) { - return sorted.map((item) => { - const { images, ...rest } = item; - - return rest; - }); - } - - return sorted; -} -``` - -Now, we can access `result[0].images` on the return value of `scrape` if **removeImages** was false without any compiler errors being thrown. But, if we switch **removeImages** to true, TypeScript will yell at us. - -![No more error](./images/no-more-error.png) - -## Final code {#final-code} - -```ts -// index.ts -import axios from 'axios'; -import { SortOrder } from './types'; - -import type { ResponseData, Product, UserInput, ModifiedProduct } from './types'; - -const fetchData = async () => { - const { data } = await axios('https://dummyjson.com/products?limit=100'); - - return data as ResponseData; -}; - -const sortData = (products: Product[], order: SortOrder) => { - switch (order) { - case SortOrder.ASC: - return [...products].sort((a, b) => a.price - b.price); - case SortOrder.DESC: - return [...products].sort((a, b) => b.price - a.price); - default: - return products; - } -}; - -async function scrape(input: UserInput): Promise; -async function scrape(input: UserInput): Promise; -async function scrape(input: UserInput) { - const data = await fetchData(); - - const sorted = sortData(data.products, input.sort as SortOrder); - - if (input.removeImages) { - return sorted.map((item) => { - const { images, ...rest } = item; - - return rest; - }); - } - - return sorted; -} - -const main = async () => { - const INPUT: UserInput = { sort: 'ascending', removeImages: false }; - - const result = await scrape(INPUT); - - console.log(result[0].images); -}; - -main(); -``` - -```ts -// types.ts -export interface Product { - id: number; - title: string; - description: string; - price: number; - discountPercentage: number; - rating: number; - stock: number; - brand: string; - category: string; - thumbnail: string; - images: string[]; -} - -export interface ResponseData { - products: Product[]; -} - -export type ModifiedProduct = Omit; - -export enum SortOrder { - ASC = 'ascending', - DESC = 'descending', -} - -export interface UserInput { - sort: 'ascending' | 'descending'; - removeImages: RemoveImages; -} -``` - -## Wrap up {#wrap-up} - -Nice work! You've reached the end of our **Switching to TypeScript** course, which means that you're ready to start building your own projects in TypeScript. We haven't covered every single TypeScript feature in this course, but you have learned about and used all of the language's most important features. - -Congrats! 🎉 diff --git a/sources/academy/webscraping/typescript/type_aliases.md b/sources/academy/webscraping/typescript/type_aliases.md deleted file mode 100644 index ba8910270b..0000000000 --- a/sources/academy/webscraping/typescript/type_aliases.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -title: Type aliases & function types -description: Create your own custom types using the "type" keyword, understand the "void" type, and learn how to write custom function types. -sidebar_position: 7.5 -slug: /switching-to-typescript/type-aliases ---- - -# Type aliases {#type-aliases} - -**Create your own custom types using the "type" keyword, understand the "void" type, and learn how to write custom function types.** - ---- - -Real quick, let's look at this code: - - -```ts -// Using a union type to allow value to be either a string, -// a number, or a boolean -const returnValueAsString = (value: string | number | boolean) => { - return `${value}`; -}; - -let myValue: string | number | boolean; - -myValue = 55; - -console.log(returnValueAsString(myValue)); -``` - -This is fine, but we had to write `string | number | boolean` twice, and if this were a large project, we'd likely find ourselves writing it even more times. The solution for this is to define the type elsewhere, giving it a name by which it can be identified, and then to use that name within the type annotations for `returnValueAsString` and `myValue`. - -## Creating types {#creating-types} - -With the `type` keyword, you can abstract all the type stuff you'd normally put in a type annotation into one **type alias**. The great thing about type aliases is that they improve code readability and can be used in multiple places. - -First, we'll use the `type` keyword and call the type `MyUnionType`. - - -```ts -type MyUnionType = any; -``` - -Then, we can just copy-paste the union type between a string, number, and boolean and paste it after the equals sign: - -```ts -type MyUnionType = string | number | boolean; -``` - -The type is now stored under this `MyUnionType` **alias**. - -> Any type declaration "logic" that you can place within a type annotation can also be stored under a type alias. - -## Using type aliases {#using-type-aliases} - -All we need to do to refactor the code from the beginning of the lesson is replace the type annotations with `MyUnionType`: - - -```ts -type MyUnionType = string | number | boolean; - -const returnValueAsString = (value: MyUnionType) => { - return `${value}`; -}; - -let myValue: MyUnionType; - -myValue = 55; - -console.log(returnValueAsString(myValue)); -``` - -## Function types {#function-types} - -Before we learn about how to write function types, let's learn about a problem they can solve. We have a function called `addAll` which takes in array of numbers, adds them all up, and then returns the result. - -```ts -const addAll = (nums: number[]) => { - return nums.reduce((prev, curr) => prev + curr, 0); -}; -``` - -We want to add the ability to choose whether or not the result should be printed to the console. A function's parameter can be marked as optional by using a question mark. - -Let's do that now. - -```ts -// We've added a return type to the function because it will return different -// things based on the "printResult" parameter. When false, a number will be -// returned, while when true, nothing will be returned (void). -const addAll = (nums: number[], printResult?: boolean): number | void => { - const result = nums.reduce((prev, curr) => prev + curr, 0); - - if (!printResult) return result; - - console.log('Result:', result); -}; -``` - -Also, it'd be nice to have some option to pass in a custom message for when the result is logged to the console, so we'll add another optional parameter for that. - -```ts -const addAll = (nums: number[], printResult?: boolean, printWithMessage?: string): number | void => { - const result = nums.reduce((prev, curr) => prev + curr, 0); - - if (!printResult) return result; - - console.log(printWithMessage || 'Result:', result); -}; -``` - -Finally, we'll add a final parameter with the option to return/print the result as a string instead of a number. - -```ts -const addAll = (nums: number[], toString?: boolean, printResult?: boolean, printWithMessage?: string): number | string | void => { - const result = nums.reduce((prev, curr) => prev + curr, 0); - - if (!printResult) return toString ? result.toString() : result; - - console.log(printWithMessage || 'Result:', toString ? result.toString : result); -}; -``` - -What we're left with is a massive function declaration that is very verbose. This isn't necessarily a bad thing, but all of these typings could be put into a function type instead. - -### Creating & using function types {#creating-and-using-function-types} - -Function types are declared with the `type` keyword (or directly within a type annotation), and are written in a similar fashion to regular arrow functions. All parameters and their types go inside the parentheses (`()`), and the return type of the function goes after the arrow (`=>`). - -```ts -type AddFunction = (numbers: number[], toString?: boolean, printResult?: boolean, printWithMessage?: string) => number | string | void; -``` - -This is where the true magic happens. Because our arrow function is stored in a variable, and because we've now created the `AddFunction` type, we can delete all type annotations from the function itself and annotate the variable with `AddFunction` instead. - -```ts -type AddFunction = (numbers: number[], toString?: boolean, printResult?: boolean, printWithMessage?: string) => number | string | void; - -const addAll: AddFunction = (nums, toString, printResult, printWithMessage) => { - const result = nums.reduce((prev, curr) => prev + curr, 0); - - if (!printResult) return toString ? result.toString() : result; - - console.log(printWithMessage || 'Result:', toString ? result.toString : result); -}; -``` - -We've significantly cleaned up the function by moving its verbose type annotations into a type alias without losing the benefits of TypeScript. - -![Parameter type](./images/parameter-type.png) - -## Next up {#next} - -A special type exists that you haven't learned about yet called `unknown`. We haven't yet discussed it, because it's best learned alongside **type casting**, which is yet another feature offered by TypeScript. Learn all about the `unknown` type and typecasting in the [next lesson](./unknown_and_type_assertions.md). diff --git a/sources/academy/webscraping/typescript/unknown_and_type_assertions.md b/sources/academy/webscraping/typescript/unknown_and_type_assertions.md deleted file mode 100644 index 1c961e072d..0000000000 --- a/sources/academy/webscraping/typescript/unknown_and_type_assertions.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: Unknown, any, type guards & type assertions -description: Understand the "unknown" and "any" types, as well as how to use type guards to make your code safer and type assertions to avoid common TypeScript compiler errors. -sidebar_position: 7.6 -slug: /switching-to-typescript/unknown-and-type-assertions ---- - -# Unknown & type assertions {#unknown-and-type-assertions} - -**Understand the "unknown" and "any" types, as well as how to use type guards to make your code safer and type assertions to avoid common TypeScript compiler errors.** - ---- - -Two types we haven't discussed yer are `any` and `unknown` - -## Let's talk about "any" {#the-any-type} - -In the first [**Using types**](./using_types.md) lesson, you were briefly exposed to the `any` type, which is a special type used to represent all possible JavaScript values. By using this type, you basically tell TypeScript that you don't care, and that you want to be able to do anything with that value, even if it might cause a runtime error. Take a look at this example: - - -```ts -// Create a variable that TypeScript will completely ignore. -// Absolutely anything can be stored in here. -let userInput: any; - -// Create a variable that can only hold strings -let savedInput: string; - -// Set the user input to equal a number. This is fine, because -// it can be any time. -userInput = 5; - -// Set the "savedInput" to be the value of "userInput". Stored in -// "userInput" is a number, but since we told TypeScript that it's -// "any" type, an error is not thrown. -savedInput = userInput; -``` - -Sometimes, `any` can be useful; however, in 99% of cases it is best to avoid it as it can lead to logical errors just like the one above. - -## Why "unknown" is better {#the-unknown-type} - -Just like `any`, the `unknown` type is also a special type that represents all possible JavaScript value, and all types are assignable to it. The big difference is that the TypeScript compiler won't allow any operation on values typed as `unknown`. To see this in action, we just have to change the type of `userInput` in the above code snippet from `any` to `unknown`. - -![Replacing "any" with "unknown" from the above snippet](./images/replace-with-unknown.png) - -Even this will result in the same error: - - -```ts -// This results in a compiler error! -let userInput: unknown; -let savedInput: string; - -userInput = 'hello world!'; - -savedInput = userInput; -``` - -## Type guards {#type-guards} - -In order to make the code above not throw any compiler errors, we can use a [type guard](https://www.typescriptlang.org/docs/handbook/advanced-types.html), which is just a check that happens at runtime to ensure that the type is in fact what it should be. - - -```ts -let userInput: unknown; -let savedInput: string; - -userInput = 5; - -// This if statement is called a "type guard" -// No more error! TypeScript is smart enough to understand -// what this if statement is doing, and removes the error -if (typeof userInput === 'string') { - savedInput = userInput; -} -``` - -This works, and in fact, it's the most optimal solution for this use case. But what if we were 100% sure that the value stored in `userInput` was a string? Thats when **type assertions** come in handy. - -## Type assertions {#type-assertions} - -Despite the fancy name, [type assertions](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions) are a concept based around a single keyword: `as`. We usually use this on values that we can't control the return type of, or values that we're sure have a certain type, but TypeScript needs a bit of help understanding that. - - -```ts -let userInput: unknown; -let savedInput: string; - -userInput = 'hello world!'; - -// No more error! We've told TypeScript to treat "userInput" -// as a string, despite the fact that its original type is -// the "unknown" type -savedInput = userInput as string; -``` - -## Non-null assertion {#non-null-assertion} - -You might already be familiar with [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) in JavaScript (`?.` syntax). TypeScript adds a new operator with a similar syntax that uses an exclamation mark instead (`!.`), which automatically removes `null` and `undefined` from a value's type - essentially asserting that you are certain it will never be `null` or `undefined`. - -Consider this snippet: - -```ts -let job: undefined | string; - -const chars = job.split(''); -``` - -TypeScript will yell at you when trying to compile this code, stating that **Object is possibly 'undefined'**, which is true. To assert that `job` will not be `undefined` in this case, we can add an exclamation mark before the dot. - -```ts -let job: undefined | string; - -const chars = job!.split(''); -``` - -This operator is called the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#non-null-assertion-operator-postfix-). - -## Final thoughts {#final-thoughts} - -Even though you've learned about them in the same lesson, type guards and type assertions are inherently very different things with different use cases. - -**Type guards** make a runtime check of whether or not a value passes a check that determines that it can be safely used as a certain type. They are great when dealing with values that might hold inconsistent data types (such as user input) where you aren't 100% sure if a certain property will exist. - -**Type assertions** tell TypeScript to take a value of one type and to treat it as if it were another type. No runtime checks are made. A common use case is asserting the response body of an API call (usually has the `any` type depending on what you're using to fetch the data) to a custom type to receive TypeScript support on the data. - -Oftentimes, these features are used in tandem. - -## Next up {#next} - -We've now got all the knowledge we need to build a real project in TypeScript, which we're going to do very soon. But, there's one important thing we have to do before writing the code for our project - configure the compiler and understand watch mode. Learn all this in the [next lesson](./watch_mode_and_tsconfig.md)! diff --git a/sources/academy/webscraping/typescript/using_types.md b/sources/academy/webscraping/typescript/using_types.md deleted file mode 100644 index 479c96a226..0000000000 --- a/sources/academy/webscraping/typescript/using_types.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Using types - I -description: Dip your toes into using types with TypeScript by learning about the core types offered by the language, and how to define variables and functions with them. -sidebar_position: 7.2 -slug: /switching-to-typescript/using-types ---- - -# Using types {#using-types} - -**Dip your toes into using types with TypeScript by learning about the core types offered by the language, and how to define variables and functions with them.** - ---- - -In the previous lesson, we got a rough idea of how types are used by utilizing the `number` type in a function's parameters. The `number` type is one of TypeScript's three core types. - -## Core types - -| Name | Example | Description | -| --------- | ----------------------- | ------------------------------------------------------------ | -| `number` | `1`, `5.3`, `-10` | All numbers. No differentiation between integers and floats. | -| `string` | `'hi'`, `"hello world"` | All text values. | -| `boolean` | `true`, `false` | Just these two. No "truthy" or "falsy" values. | - -## With variables {#with-variables} - -Just because we're writing TS files doesn't mean we need to explicitly define the type of every single value/parameter though. We'll create a new file called **using-types.ts** and create a basic variable: - -```ts -const value = 10; -``` - -When hovering over the variable, we see that TypeScript was smart enough to infer that the data type stored in `value` should always be a number. - -![Type of variable automatically inferred](./images/number-inference.png) - -Attempting to reassign `value` to a type other than a number will result in a compiler error. - -But what if we want to declare the variable with no initial value, and then change it later? - - -```ts -let value; - -value = 10; -``` - -TypeScript can't automatically infer the type of the variable when we don't provide it an initial value, so it automatically uses the `any` type. - -> Note: Avoid using the `any` type as much as possible. It completely defeats the purpose of using TypeScript in the first place, as it removes the benefits of TS. - -Because of this, we can set `value` to be absolutely anything without receiving any compiler errors. - -![TypeScript didn't infer the type](./images/is-any.png) - -To resolve this, we can annotate the variable by adding a colon (`:`) after the name followed by the name of the type we'd like to be tied to the variable. - -```ts -let value: number; - -// Totally ok -value = 10; - -// This will throw a compiler error -value = 'hello academy!'; -``` - -To allow for the `value` variable to hold multiple different types, we can use a [union type](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html). It works just the same way as the **or** (`||`) operator in JavaScript, but only uses one pipe (`|`) character and only works with types and type annotations. - -```ts -// "value" can hold either a number or a string -let value: number | string; - -// Totally ok -value = 10; - -// Totally ok -value = 'hello academy!'; - -// This will throw a compiler error, because we didn't include -// number arrays in our union type. -value = [1, 2, 3]; -``` - -Later in this course, we'll be getting more into union types. - -## With functions {#with-functions} - -With functions, you can define the types of both the parameters and the return type. Here's a basic function: - -```ts -const totalLengthIsGreaterThan10 = (string1, string2) => { - // Returns true if the total length of both strings is greater - // than 10, and false if it's less than 10. - return (string1 + string2).length > 10; -}; -``` - -Just like with the parameters in the function from the last lesson, and similar to variables, these parameters can be annotated with a colon (`:`) and a type name. In this case, we are expecting two strings into this function. - -```ts -const totalLengthIsGreaterThan10 = (string1: string, string2: string) => { - return (string1 + string2).length > 10; -}; -``` - -The return value of this function is a boolean, which TypeScript has intelligently inferred. - -![Return type inferred](./images/return-inferred.png) - -Despite the correct inference, if we wanted to explicitly annotate this function's return type, we could. Return type annotations go after the parentheses (`()`) where the function's parameters are defined. - -```ts -const totalLengthIsGreaterThan10 = (string1: string, string2: string): boolean => { - return (string1 + string2).length > 10; -}; -``` - -For non-arrow functions, the type annotation syntax is the exact same: - -```ts -function totalLengthIsGreaterThan10(string1: string, string2: string): boolean { - return (string1 + string2).length > 10; -} -``` - -## Next up {#next} - -[Next up](./using_types_continued.md), we'll discuss a few more basic types supported in TypeScript and how to use them. diff --git a/sources/academy/webscraping/typescript/using_types_continued.md b/sources/academy/webscraping/typescript/using_types_continued.md deleted file mode 100644 index eda345b706..0000000000 --- a/sources/academy/webscraping/typescript/using_types_continued.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: Using types - II -description: Continue learning about the core types in TypeScript. In this second part lesson, learn how to use and define object types, array types, and tuples. -sidebar_position: 7.3 -slug: /switching-to-typescript/using-types-continued ---- - -# Using types (continued) {#using-types-continued} - -**Continue learning about the core types in TypeScript. In this second part lesson, learn how to use and define object types, array types, and tuples.** - ---- - -Now that you're (hopefully) fairly comfortable with strings, booleans, and numbers in TypeScript, we can start discussing the more complex types supported by the language. - -| Name | Example | Description | -| -------- | ------------------------------ | ------------------------------------------------------------------------------------ | -| `object` | `{ name: 'academy' }` | Any JavaScript object. More specific types are possible. | -| `Array` | `[1, 2, 3]`, `['a', 'b', 'c']` | Any JavaScript array. Types can be flexible or strict (regarding the element types). | -| `Tuple` | `[1, 2]` | A fixed-length array. | - -## Objects {#objects} - -By now, you're already familiar with what type inference is. Even when creating objects, TypeScript will do its best to infer what that object's type is. For example, TS automatically infers correctly that this object is an object with `name` and `currentLesson` properties, both strings: - -![Object type correctly inferred](./images/object-inference.png) - -Notice that the value that appeared in the popup when hovering over the `course` variable looks very much like a regular JavaScript object; however, it is not an object at all - instead, it is an object **type**. - -> Object types differ slightly in syntax from regular JavaScript objects. Firstly, the properties are separated by semicolons (`;`) instead of commas. Also, instead of being key-value pairs like objects, object types are key-**type** pairs. - -This inference from TypeScript is totally valid and correct; however, what if we want to add a new property to the `course` object after it's been initialized? - -![Error adding a new property to the object](./images/object-type-error.png) - -What's the problem? The problem is that we didn't tell TypeScript that the `course` object can have a property called `learningBasicTypes` of a boolean type in the variable's initial state, which is what TypeScript's inference is based on. Because of this, we have to write our own custom object type. - -Just as we did before when assigning types like `number` and `string` to variables, we'll annotate the variable's type with a colon (`:`) followed by the type. However, instead of using a basic type name such as `boolean` or `number`, we'll put a custom object type there instead. - -```ts -const course: { - name: string; - currentLesson: string; -} = { - name: 'Switching to TypeScript', - currentLesson: 'Using types - II', -}; -``` - -> We know, this looks extremely weird at first, but by the end of the course you'll be super comfortable with this syntax. And, a bit later on in the course, you'll learn how to define object types separate from the variable to improve code readability. - -### Optional properties {#optional-properties} - -Finally, we'll add an optional property to our object type with a key of `learningBasicTypes` and a type of `boolean`. Properties become optional by adding a question mark (`?`) before the colon (`?`) after the property name. - -```ts -// Initialize the object without the "learningBasicTypes" property -const course: { - name: string; - currentLesson: string; - // Add the "learningBasicTypes" property to the object's type. - // This property can be either undefined or boolean - learningBasicTypes?: boolean; -} = { - name: 'Switching to TypeScript', - currentLesson: 'Using types - II', -}; - -// Add the "learningBasicTypes" property later on -course.learningBasicTypes = true; -``` - -What this question mark does is tell TypeScript that the property doesn't necessarily have to exist on the `course` object (it can be undefined), but if it does exist, it must be a boolean. - -## Arrays {#arrays} - -Defining arrays is quite straightforward. We'll first add a `typesLearned` property to our `course` object: - -```ts -const course2 = { - name: 'Switching to TypeScript', - currentLesson: 'Using types - II', - typesLearned: ['number', 'boolean', 'string', 'object'], -}; -``` - -Then, in the type definition, we can add a `typesLearned` key. Then, by writing the type that the array's elements are followed by two square brackets (`[]`), we can form an array type. - -```ts -const course: { - name: string; - currentLesson: string; - // By adding square brackets at the end, we tell TypeScript - // that "typesLearned" is not a string, but an array of strings - typesLearned: string[]; - learningBasicTypes?: boolean; -} = { - name: 'Switching to TypeScript', - currentLesson: 'Using types - II', - typesLearned: ['number', 'boolean', 'string', 'object'], -}; -``` - -Some of the benefits of TypeScript can be seen when accessing one of the properties on the `course.typesLearned` array. Since we told TypeScript that it's an array of strings, it confidently knows that each of those properties are going to have the methods on the [String prototype](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#instance_methods), so it will autocomplete methods such as `.slice()`, `.toUpperCase()`, and `.slice()`. - -![Epic autocomplete](./images/epic-autocomplete.png) - -Nice! - -## Tuples {#tuples} - -The **Tuple** type is a special type that is not in vanilla JavaScript, but is supported in other programming languages - including TypeScript. It is almost the same thing as an array; however tuples have a fixed length that cannot be changed, and each element at each index has a specific type. Don't worry, this type is much easier to understand in practice. - -Let's add a final property to our epic `course` object called `courseInfo`. This property will hold an array where the first value corresponds to this course's number in the Apify academy, and the value in the second position describes the level of this course. - -```ts -const course = { - name: 'Switching to TypeScript', - currentLesson: 'Using types - II', - typesLearned: ['number', 'boolean', 'string', 'object'], - courseInfo: [7, 'advanced'], -}; -``` - -Here's one way we could define this property's type: - -```ts -const course: { - name: string; - currentLesson: string; - typesLearned: string[]; - // an array of numbers or strings - courseInfo: (number | string)[]; - learningBasicTypes?: boolean; -} = { - name: 'Switching to TypeScript', - currentLesson: 'Using types - II', - typesLearned: ['number', 'boolean', 'string', 'object'], - courseInfo: [7, 'advanced'], -}; -``` - -This is actually how TypeScript infers the type of `courseInfo` as well. It tells the compiler that `courseInfo` is an array of any length that can hold both numbers and strings. However, we want to be more specific than that in this case. The length of `courseInfo` should always be 2, the first value should always be a number, and the second one always be a string. We should use a tuple instead. - -```ts -const course: { - name: string; - currentLesson: string; - typesLearned: string[]; - // an array with 2 elements. courseInfo[0] is a string, - // while courseInfo[0] is a number - courseInfo: [number, string]; - learningBasicTypes?: boolean; -} = { - name: 'Switching to TypeScript', - currentLesson: 'Using types - II', - typesLearned: ['number', 'boolean', 'string', 'object'], - courseInfo: [7, 'advanced'], -}; -``` - -By placing types inside of the square brackets in a specific order, we tell TypeScript that the property is not of an array type, but a tuple instead. When we try to reassign the properties to an incorrect data type, we get an error. - -![Epic autocomplete](./images/string-not-number.png) - -## Next up {#next} - -Whew! Nice job learning about the core types in TypeScript. The next lesson is lighter than the last two, but still super important. [Let's learn](./enums.md) about the `enum` keyword! diff --git a/sources/academy/webscraping/typescript/watch_mode_and_tsconfig.md b/sources/academy/webscraping/typescript/watch_mode_and_tsconfig.md deleted file mode 100644 index b3ceaee778..0000000000 --- a/sources/academy/webscraping/typescript/watch_mode_and_tsconfig.md +++ /dev/null @@ -1,319 +0,0 @@ ---- -title: Watch mode & tsconfig.json -description: Learn how to fine-tune TypeScript for an entire project's needs and efficiently compile numerous TS files at a single time (automagically). -sidebar_position: 7.7 -slug: /switching-to-typescript/watch-mode-and-tsconfig ---- - -# Watch mode and tsconfig.json {#watch-mode-and-tsconfig} - -**Learn how to fine-tune TypeScript for an entire project's needs and efficiently compile numerous TS files at a single time (automagically).** - ---- - -Thus far, each time we've made changes to our TypeScript code, we've had to run the `tsc FILE_NAME.ts` command in the terminal. Very quickly, this becomes repetitive and cumbersome, especially for large projects with more than one file. Luckily, the TypeScript compiler has a special feature called **watch mode**, which will watch a specific file (or all **.ts** files) for any changes. If any changes are made, it will automatically recompile. - -> Test out watch mode on a single file by using the `--watch` (or `-w` for short) flag like so: `tsc FILE_NAME --watch`. - -## tsconfig.json {#tsconfig} - -If your project has more than one file, it's necessary to have a `tsconfig.json` file at the root of your project. This is a file which allows you to configure TypeScript to your liking, as well as utilize a "general" watch mode that watches all TS files and recompiles when changes are made. - -### Creating the file {#creating-the-file} - -In the next lesson, we'll be learning how to use interfaces in combination with type casting and a few other concepts from the previous lessons by building a small project. Let's create a new directory for this project right now and call it **my-first-typescript-project**. Within the directory, we'll first initialize the project with this command: - -```shell -npm init -y -``` - -Then, in order to tell TypeScript that this is a whole project, we'll run this command: - -```shell -tsc --init -``` - -Notice that a new **tsconfig.json** file has been automatically created. When you open it up, here's what you'll see: - -```json -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "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. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} -``` - -### Bare basic configurations {#bare-basic-configurations} - -As you can see, there are a whole lot of options, which is quite overwhelming. Don't worry, we'll be walking you through all of the important ones, so for now let's delete all of the contents of this **tsconfig.json** and start from scratch. - -#### Excluding files and folders {#excluding-files-and-folders} - -It is possible to tell TypeScript which files to compile, and which ones to ignore. The **exclude** option in **tsconfig.json** holds an array of file/folder names/paths that should **not** be watched. - -```json -{ - "compilerOptions": {}, - "exclude": ["node_modules"] -} -``` - -In our case, we don't want to compile any TypeScript code that could possibly be living in the **node_modules** folder that will appear when we start installing dependencies, so we've added it to the array. - -#### Telling TypeScript which files to compile {#telling-typescript-what-to-compile} - -Along with the **exclude** property is the **include** property, which holds an array of files/paths to check when compiling. Anything not included within the array will be ignored. - -In the next project, we are going to follow a very common pattern with TypeScript projects by keeping all of our TS files in a folder named **src**. Let's create a **src** folder within **my-first-typescript-project**, then add its path to the **include** property's array. - -```json -{ - "compilerOptions": {}, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -#### Specify where to put compiled files {#specify-where-to-put-compiled-files} - -It's common practice in TypeScript projects to keep **.ts** files separate from their respective compiled **.js** files. Usually, the compiled files are placed in a folder named **dist** or **build**. Let's use **dist** for our project. - -Within **compilerOptions**, the **outDir** property tells TypeScript just that - where to place all compiled files. - -```json -{ - "compilerOptions": { - "outDir": "dist/" - }, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -This time, you don't have to manually create a folder named **dist**. During compile time, TypeScript will detect whether or not the folder exists and automatically create it if it doesn't. - -> We also recommend learning about [**rootDir**](https://www.typescriptlang.org/tsconfig/#rootDir). - -### Important basic configurations {#important-basic-configurations} - -Other than telling TypeScript **what** files it should (and should not) compile, we also need to tell it **how** they should be compiled. - -#### Setting the target {#setting-the-target} - -**target** within **compilerOptions** tells TypeScript which JavaScript version you'd like to compile your code into. This allows for the ability to, for example, use ES7 features during development time, but support environments that only work with the ES3 version of JavaScript. We'll use **esnext**. - -```json -{ - "compilerOptions": { - "target": "esnext", - "outDir": "dist/" - }, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -#### Setting libs {#setting-libs} - -By default TypeScript will allow us to use things like `document.querySelector()` or `window.reload()` even though we're writing Node.js code where those global objects don't exist. This is because TypeScript automatically has these libraries enabled. In order to prevent this, we'll get more specific about the **lib**s we'd like to use. - -```json -{ - "compilerOptions": { - "target": "esnext", - "lib": ["ES2015", "ES2016", "ES2018", "ES2019.Object", "ES2018.AsyncIterable", "ES2020.String", "ES2019.Array"], - "outDir": "dist/" - }, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -> Learn more about the **lib** configuration option [in the TypeScript documentation](https://www.typescriptlang.org/tsconfig/#lib). - -#### Removing comments {#removing-comments} - -This one is pretty straightforward. **removeComments** allows you to keep the comments which are useful in the code during development out of your compiled files. - -```json -{ - "compilerOptions": { - "target": "esnext", - "lib": ["ES2015", "ES2016", "ES2018", "ES2019.Object", "ES2018.AsyncIterable", "ES2020.String", "ES2019.Array"], - "outDir": "dist/", - "removeComments": true - }, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -#### Refusing to compile if there are any errors {#dont-compile-if-errors} - -In most statically typed programming languages, the compiler will refuse to produce an output until all errors have been fixed; however, TypeScript by default will still compile even if there are errors. To enable the more strict functionality that other languages support, set **noEmitOnError** to **true**. - -```json -{ - "compilerOptions": { - "target": "esnext", - "lib": ["ES2015", "ES2016", "ES2018", "ES2019.Object", "ES2018.AsyncIterable", "ES2020.String", "ES2019.Array"], - "outDir": "dist/", - "removeComments": true, - "noEmitOnError": true - }, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -#### Adding strict type checking {#strict-type-checking} - -TypeScript has [multiple options](https://learntypescript.dev/11/l6-strictness) for strict type checking that can be configured. To enable all of them, set **strict** to **true** (this is recommended). - -```json -{ - "compilerOptions": { - "target": "esnext", - "lib": ["ES2015", "ES2016", "ES2018", "ES2019.Object", "ES2018.AsyncIterable", "ES2020.String", "ES2019.Array"], - "outDir": "dist/", - "removeComments": true, - "noEmitOnError": true, - "strict": true - }, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -#### Setting module resolution & type {#module-resolution} - -By default, TypeScript doesn't know how to handle or recognize modules imported into our projects. We'll tell the compiler it's a Node.js project with the **moduleResolution** option set to **node**, and that we are using CommonJS for the module type. - -```json -{ - "compilerOptions": { - "target": "esnext", - "lib": ["ES2015", "ES2016", "ES2018", "ES2019.Object", "ES2018.AsyncIterable", "ES2020.String", "ES2019.Array"], - "outDir": "dist/", - "removeComments": true, - "noEmitOnError": true, - "strict": true, - "moduleResolution": "node", - "skipLibCheck": true, /* this will prevent TS errors from happening due to library errors */ - "module": "commonjs" - }, - "exclude": ["node_modules"], - "include": ["src/"] -} -``` - -## Watch mode {#watch-mode} - -Now that you've finished configuring the **tsconfig.json** file, go ahead and create an **index.ts** file in the **src** folder. Because we've configured this project with TypeScript, we can just run this command: - -```shell -## -w is the shortened version of the --watch flag -tsc -w -``` - -And our files in **src** will automatically compile into a folder named **dist**, and one that change will be recompiled. - -## Next up {#next} - -Now that we're all set up, we can move forward and start building our mini-project. But first, [let's learn](./interfaces.md) about the `interface` keyword!