Skip to content

Improve Concept Exercise: Type Conversion #1248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions concepts/conditionals/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ if (isPositive && isSmall) {

In JavaScript the condition does not have to be of type boolean.
If any other type than boolean is provided in a boolean context like the if-statement, JavaScript will implicitly convert the value to boolean.
Refer to the [type coercion concept][concept-type-coercion] for details on which values are _truthy_ and _falsy_, respectively.
Refer to the [type conversion concept][concept-type-conversion] for details on which values are _truthy_ and _falsy_, respectively.

```javascript
const num = 4;
Expand Down Expand Up @@ -137,5 +137,5 @@ function checkNumber(num) {
}
```

[concept-type-coercion]: /tracks/javascript/concepts/type-coercion
[concept-type-conversion]: /tracks/javascript/concepts/type-conversion
[mdn-operator-precedence]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table
4 changes: 2 additions & 2 deletions concepts/type-conversion/.meta/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"blurb": "TODO: add blurb for spread-operator concept",
"authors": ["shubhsk88"],
"blurb": "Changing the type of a variable can be by done by explicit type conversion. Besides that, JavaScript also performs type coercion (implicit type conversion) in certain contexts.",
"authors": ["shubhsk88", "junedev"],
"contributors": ["neenjaw", "SleeplessByte"]
}
261 changes: 256 additions & 5 deletions concepts/type-conversion/about.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,261 @@
# About

JavaScript values can be of many types, when performing operations or comparisons, types need to either be _explicitly converted_ to the same type, or _implicitly coerced_ to the same type.
In JavaScript, values may be of different types. Changing the type of a variable can be done by explicit _type conversion_.
Besides that, JavaScript also performs _type coercion_ (implicit type conversion) when the context requires it.

Generally, type coercion is avoided because it is averse to unexpected errors. Instead, opt for practices which allow for explicit type conversion.
## Type Conversion

An overview of these terms can be found on _MDN Web Docs_:
JavaScript does not have a construct to cast into a (different) type like many other languages but there are built-in helpers that can be used instead.
Most notably, the global objects `Boolean`, `Number` and `String` can be used as functions to convert a value to the respective type.

- [Type Conversion](https://developer.mozilla.org/en-US/docs/Glossary/Type_Conversion)
- [Type Coercion](https://developer.mozilla.org/en-US/docs/Glossary/Type_coercion)
### Converting to a Boolean (Truthy/Falsy Values)

With `Boolean(value)` you can convert any value to a boolean.
How does that work?

There is a fixed set of values, so called _falsy_ values, that convert to `false`.
Most importantly, `false`, `0`, empty string, `null`, `undefined` and `NaN` are falsy.
The [MDN article on "Falsy"][mdn-falsy] shows the complete list.

For all other values, `Boolean` returns `true`.
These values are called _truthy_.

```javascript
Boolean(-1);
// => true

Boolean(0);
// => false

Boolean(' ');
// => true

Boolean('');
// => false
```

Note that because of the described rules, `'0'`, `'false'`, `[]` and `{}` are **thruthy** in JavaScript.

### Converting to a Number

`Number(value)` can be used to convert a value to a number.
Whitespaces at the beginning and the end of a string are ignored and an empty string is converted to `0`.
If you try to convert a non-primitive value or a string that does not represent a number, **no** error will be thrown.
Instead, the result is `NaN` ([Not-A-Number][mdn-nan]).

```javascript
Number(' -12.34 ');
// => -12.34

Number('1,2');
// => NaN

Number('1 2');
// => NaN

Number('');
// => 0

Number('10e3');
// => 10000

Number({ key: '123' });
// => NaN
```

Below you can see what `Number` returns for other primitive values.

```javascript
Number(true);
// => 1

Number(false);
// => 0

Number(null);
// => 0

Number(undefined);
// => NaN
Comment on lines +78 to +79
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So Number(undefined) is NaN, but Number() is 0? 🤯

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right but I didn't know this either 🤯

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a sentence about this behavior. I couldn't find an explanation though.
@SleeplessByte Do you know what's going on there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do. It's specced

Number ( [ value ] )

Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else returns +0.

So this has a special case for no argument, which is +0.

Then we check ToNumber:

Argument Type Result
Undefined NaN
Null +0
Boolean The result is 1 if the argument is true. The result is +0 if the argument is false.
Number The result equals the input argument (no conversion).
String See grammar and note below.
Object Apply the following steps:Let primValue be ToPrimitive(input argument, hint Number).Return ToNumber(primValue).

and thus explicitly passing undefined is a different result (NaN) than passing nothing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got that part but I was wondering why it was specced that way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably legacy / new String, new Array, new Number should be "the same" as in create an "empty" value of that type ('', [], and +0). It would be weird if that created a NaN. However passing in undefined makes it subject to coercion. Something like that.

```

Note that in contrast to the last example, `Number()` called without any argument is defined to return `0`.

JavaScript also provides the functions `parseInt` and `parseFloat`.
They apply laxer rules as to which strings can be converted to a number.
Because of that, `Number` should be preferred as conversion function to avoid unexpected outcomes.

```javascript
parseInt('123a45');
// => 123

Number('123a45');
// => NaN
```

### Converting to a String

With `String(value)` you can convert a value to a string.
The result is what you would expect it to be for primitive values.

```javascript
String(12.34);
// => '12.34'

String(false);
// => 'false'

String(null);
// => 'null'

String(undefined);
// => 'undefined'
```

For arrays, the `String` function will apply the string conversion for each element and join the results with a comma.
You can also apply the [`join` method][mdn-join] yourself, e.g. to customize the separator.
However, in these cases `null` and `undefined` will be converted to an empty string.

```javascript
String([42, null, true, 'abc']);
// => '42,,true,abc'
```

For objects, by default `String` returns an unhelpful text.

```javascript
String({ key: 'value' });
// => '[object Object]'
```

You can customize the conversion behavior, e.g. by providing a `toString` method.
The section "[Object to primitive conversion][custom-conversion]" on javascript.info explains the details.

Another common way to achieve a better string representation for objects and arrays is to use [JSON encoding][json].
You can convert into a string with `JSON.stringify` and back into an object or array with `JSON.parse`.

```javascript
const obj = {
name: 'Gilda Guerra',
address: {
city: 'Goiânia',
},
};

JSON.stringify(obj);
// => '{"name":"Gilda Guerra","address":{"city":"Goiânia"}}'
```

## Type Coercion

In certain contexts, JavaScript will automatically convert a value to another data type before it evaluates some statement.
This implicit conversion is called _type coercion_.

### Boolean Context

When a value is used in a boolean context, JavaScript will apply the same rules as the `Boolean` function to implicitly convert the value.

- When the condition of an [if statement][concept-conditionals] is not a boolean, coercion is applied to determine whether the condition is fulfilled or not.
The same applies for the first operand of the [ternary operator][mdn-ternary] `?`.

```javascript
const num = 0;
if (num) {
// this block NOT is executed because 0 is falsy
}

const name = 'Jin';
name ? 'name was provided' : 'no name provided';
// => 'name was provided'
```

- The operand of the logical NOT operator `!` is also coerced into boolean before the NOT operation is applied.

```javascript
const name = '';
!name;
// => true
```

A result of the described behavior is that `!!value` has the same effect as `Boolean(value)`.
Nevertheless, you should use `Boolean` for readability.

- JavaScript also applies coercion for the operands of the logical AND (`&&`) and OR (`||`) operators.
But keep in mind that the result of the expression is **not** necessarily a boolean.
It returns one of the original operands (see [MDN on Logical Operators][mdn-logical-operators]).

```javascript
null || 'hello';
// => 'hello'
```

### String Context

If the addition operator `+` is used for primitive values and one operand is a string, the other one will be coerced into a string as well.
The conversion logic is the same as when using the `String` function.
Afterwards, the two strings are concatenated.

```javascript
let name;
'hello ' + name;
// => 'hello undefined'
```

The same implicit conversion happens for non-string values that are embedded in [template strings][mdn-template-strings].

```javascript
const degrees = 23;
`It is ${degrees} °C`;
// => 'Is is 23 °C.';
```

### Numeric Context

There are many operators that coerce the operands into numbers (if necessary) according to the logic of the `Number` function explained above.

- Arithmetic operators: `+` (if no string is involved), `-`, `*`, `/`, `%`, `**`
- Unary plus and unary negation operators: `+`, `-`
- Relational operators (for non-string operands): `>`, `>=`, `<`, `<=`
- Bitwise operators: `|`, `&`, `^`, `~`

Refer to the [MDN list of operators][mdn-operators] for more details about any of those operators.

When an operand could potentially be a string, it is best to always explicitly convert with the `Number` function to avoid mistakes.

```javascript
'1' + '2';
// => '12'
// addition operator in string context as explained above

Number('1') + Number('2');
// => 3
```

Sometimes you will see the unary plus operator being used to coerce a string into a number.
This is not recommended because it is much harder to read then the explicit `Number` call.

```javascript
const value = '42';
+value;
// => 42

Number(value);
// => 42
```

Using the loose equality and inequality operators `==`/`!=` also often involves an implicit conversion to a number.
However, the exact logic of these operators is rather complicated (see [MDN on loose equality][mdn-loose-equality]).
The results are hard to predict and sometimes not what you would expect.
Use the strict equality/inequality operators `===`/`!==` instead to avoid these implicit conversions.

[mdn-falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy
[mdn-nan]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NaN
[mdn-join]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
[custom-conversion]: https://javascript.info/object-toprimitive
[json]: https://javascript.info/json#json-stringify
[concept-conditionals]: /tracks/javascript/concepts/conditionals
[mdn-logical-operators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#logical_operators
[mdn-ternary]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
[mdn-template-strings]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
[mdn-operators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators
[mdn-loose-equality]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#loose_equality_using_
Loading