|
1 | 1 | ---
|
2 |
| -title: Unknown & type casting |
| 2 | +title: Unknown, any, type guards & type casting |
3 | 3 | description: Create your own custom types using the "type" keyword, understand the "void" type, and learn how to write custom function types.
|
4 | 4 | menuWeight: 7.6
|
5 | 5 | paths:
|
|
8 | 8 |
|
9 | 9 | ## [](#unknown-and-type-casting) Unknown & type casting
|
10 | 10 |
|
11 |
| -In the first [**Using types**]({{@link switching_to_typescript/using_types.md}}) lesson, you were briefly exposed to the `any` type. |
| 11 | +There are two types we haven't discussed yet - `any` and `unknown`. |
| 12 | + |
| 13 | +## [](#the-any-type) Let's talk about "any" |
| 14 | + |
| 15 | +In the first [**Using types**]({{@link switching_to_typescript/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: |
| 16 | + |
| 17 | +```TypeScript |
| 18 | +// Create a variable that TypeScript will completely ignore. |
| 19 | +// Absolutely anything can be stored in here. |
| 20 | +let userInput: any; |
| 21 | + |
| 22 | +// Create a variable that can only hold strings |
| 23 | +let savedInput: string; |
| 24 | + |
| 25 | +// Set the user input to equal a number. This is fine, because |
| 26 | +// it can be any time. |
| 27 | +userInput = 5; |
| 28 | + |
| 29 | +// Set the "savedInput" to be the value of "userInput". Stored in |
| 30 | +// "userInput" is a number, but since we told TypeScript that it's |
| 31 | +// "any" type, an error is not thrown. |
| 32 | +savedInput = userInput; |
| 33 | +``` |
| 34 | + |
| 35 | +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. |
| 36 | + |
| 37 | +## [](#the-unknown-type) Why "unknown" is better |
| 38 | + |
| 39 | +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`. |
| 40 | + |
| 41 | + |
| 42 | + |
| 43 | +Even this will result in the same error: |
| 44 | + |
| 45 | +```TypeScript |
| 46 | +// This results in a compiler error! |
| 47 | +let userInput: unknown; |
| 48 | +let savedInput: string; |
| 49 | + |
| 50 | +userInput = 'hello world!'; |
| 51 | + |
| 52 | +savedInput = userInput; |
| 53 | +``` |
| 54 | + |
| 55 | +## [](#type-guards) Type guards |
| 56 | + |
| 57 | +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. |
| 58 | + |
| 59 | +```TypeScript |
| 60 | +let userInput: unknown; |
| 61 | +let savedInput: string; |
| 62 | + |
| 63 | +userInput = 5; |
| 64 | + |
| 65 | +// This if statement is called a "type guard" |
| 66 | +// No more error! TypeScript is smart enough to understand |
| 67 | +// what this if statement is doing, and removes the error |
| 68 | +if (typeof userInput === 'string') { |
| 69 | + savedInput = userInput; |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +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 casting** comes in handy. |
| 74 | + |
| 75 | +## [](#type-casting) Type casting |
| 76 | + |
| 77 | +Despite the fancy name, [type casting](https://www.typescripttutorial.net/typescript-tutorial/type-casting/) is a simple 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. |
| 78 | + |
| 79 | +```TypeScript |
| 80 | +let userInput: unknown; |
| 81 | +let savedInput: string; |
| 82 | + |
| 83 | +userInput = 'hello world!'; |
| 84 | + |
| 85 | +// No more error! We've told TypeScript to treat "userInput" |
| 86 | +// as a string, despite the fact that its original type is |
| 87 | +// the "unknown" type |
| 88 | +savedInput = userInput as string; |
| 89 | +``` |
| 90 | + |
| 91 | +## [](#final-thoughts) Final thoughts |
| 92 | + |
| 93 | +Even though you've learned about them in the same lesson, type guards and type casting are inherently very different things with different use cases. |
| 94 | + |
| 95 | +**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. |
| 96 | + |
| 97 | +**Type casting** tells 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 casting 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. |
| 98 | + |
| 99 | +Often times, these features are used in tandem. |
| 100 | + |
| 101 | +## [](#next) Next up |
| 102 | + |
| 103 | +some content |
0 commit comments