Skip to content

Commit 0d369c2

Browse files
committed
9b tweaks
1 parent 54650e0 commit 0d369c2

File tree

1 file changed

+70
-13
lines changed

1 file changed

+70
-13
lines changed

src/content/9/en/part9b.md

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -578,12 +578,13 @@ Let's specify the following configurations in our <i>tsconfig.json</i> file:
578578
```json
579579
{
580580
"compilerOptions": {
581-
"target": "ES2020",
581+
"target": "ES2022",
582582
"strict": true,
583583
"noUnusedLocals": true,
584584
"noUnusedParameters": true,
585585
"noImplicitReturns": true,
586586
"noFallthroughCasesInSwitch": true,
587+
"noImplicitAny": true, // highlight-line
587588
"esModuleInterop": true,
588589
"moduleResolution": "node"
589590
}
@@ -687,7 +688,7 @@ This is because we banned unused parameters in our <i>tsconfig.json</i>:
687688
```js
688689
{
689690
"compilerOptions": {
690-
"target": "ES2020",
691+
"target": "ES2022",
691692
"strict": true,
692693
"noUnusedLocals": true,
693694
"noUnusedParameters": true, // highlight-line
@@ -806,15 +807,22 @@ Let's add the HTTP POST endpoint *calculate* to our app:
806807
```js
807808
import { calculator } from './calculator';
808809
810+
app.use(express.json());
811+
809812
// ...
810813
811814
app.post('/calculate', (req, res) => {
812815
const { value1, value2, op } = req.body;
813816
814817
const result = calculator(value1, value2, op);
815-
res.send(result);
818+
res.send({ result });
816819
});
820+
```
821+
822+
To get this working, we must add an <i>export</i> to the function _calculator_:
817823
824+
```js
825+
export const calculator = (a: number, b: number, op: Operation) : number => {
818826
```
819827
820828
When you hover over the *calculate* function, you can see the typing of the *calculator* even though the code itself does not contain any typings:
@@ -835,15 +843,13 @@ We can also explicitly type things *any*. The only difference between the implic
835843
Programmers however see the code differently when *any* is explicitly enforced than when it is implicitly inferred.
836844
Implicit *any* typings are usually considered problematic, since it is quite often due to the coder forgetting to assign types (or being too lazy to do it), and it also means that the full power of TypeScript is not properly exploited.
837845
838-
This is why the configuration rule [noImplicitAny](https://www.typescriptlang.org/tsconfig#noImplicitAny) exists on the compiler level, and it is highly recommended to keep it on at all times.
839-
In the rare occasions when you truly cannot know what the type of a variable is, you should explicitly state that in the code:
846+
This is why the configuration rule [noImplicitAny](https://www.typescriptlang.org/tsconfig#noImplicitAny) exists on the compiler level, and it is highly recommended to keep it on at all times. In the rare occasions when you truly cannot know what the type of a variable is, you should explicitly state that in the code:
840847
841848
```js
842849
const a : any = /* no clue what the type will be! */.
843850
```
844851
845-
We already have <i>noImplicitAny</i> configured in our example, so why does the compiler not complain about the implicit *any* types?
846-
The reason is that the *query* field of an express [Request](https://expressjs.com/en/5x/api.html#req) object is explicitly typed *any*. The same is true for the *request.body* field we use to post data to an app.
852+
We already have <i>noImplicitAny: true</i> configured in our example, so why does the compiler not complain about the implicit *any* types? The reason is that the *body* field of an Express [Request](https://expressjs.com/en/5x/api.html#req) object is explicitly typed *any*. The same is true for the *request.query* field that Express uses for the query parameters.
847853
848854
What if we would like to restrict developers from using the *any* type? Fortunately, we have methods other than <i>tsconfig.json</i> to enforce a coding style. What we can do is use <i>ESlint</i> to manage
849855
our code.
@@ -934,7 +940,7 @@ Quite a few semicolons are missing, but those are easy to add. We also have to s
934940
935941
We could and probably should disable some ESlint rules to get the data from the request body.
936942
937-
Disabling *@typescript-eslint/no-unsafe-assignment* for the destructuring assignment is nearly enough:
943+
Disabling *@typescript-eslint/no-unsafe-assignment* for the destructuring assignment and calling the [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/Number) constructor to values is nearly enough:
938944
939945
```js
940946
app.post('/calculate', (req, res) => {
@@ -943,8 +949,8 @@ app.post('/calculate', (req, res) => {
943949
// highlight-end
944950
const { value1, value2, op } = req.body;
945951
946-
const result = calculator(Number(value1), Number(value2), op);
947-
res.send(result);
952+
const result = calculator(Number(value1), Number(value2), op); // highlight-line
953+
res.send({ result });
948954
});
949955
```
950956
@@ -963,7 +969,7 @@ app.post('/calculate', (req, res) => {
963969
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
964970
// highlight-end
965971
const result = calculator(Number(value1), Number(value2), op);
966-
res.send(result);
972+
res.send({ result });
967973
});
968974
```
969975
@@ -975,7 +981,7 @@ app.post('/calculate', (req, res) => {
975981
const { value1, value2, op } = req.body;
976982
977983
// highlight-start
978-
if ( !value1 || isNaN(Number(value1))) {
984+
if ( !value1 || isNaN(Number(value1)) ) {
979985
return res.status(400).send({ error: '...'});
980986
}
981987
// highlight-end
@@ -984,10 +990,61 @@ app.post('/calculate', (req, res) => {
984990
985991
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
986992
const result = calculator(Number(value1), Number(value2), op);
987-
return res.send(result);
993+
return res.send({ result });
988994
});
989995
```
990996
997+
We shall see later on in this parts some techniques how the <i>any</i> typed data (eg. the input an app recieves from the user) can be <i>narrowed</i> to a more specific type (such as number). With a proper narrowing of types, there is no more need to silence the eslint rules.
998+
999+
### Type assertion
1000+
1001+
Using a [type assertion](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions) is another "dirty trick" that can be done to keep TypeScript compiler and Eslint quiet. Let us export the type Operation in <i>calcultor.ts</i>:
1002+
1003+
```js
1004+
export type Operation = 'multiply' | 'add' | 'divide';
1005+
```
1006+
1007+
Now we can import the type and use a <i>type assertion</i> to tell the TypeScript compiler what type a variable has:
1008+
1009+
```js
1010+
import { calculator, Operation } from './calculator'; // highligh-line
1011+
1012+
app.post('/calculate', (req, res) => {
1013+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1014+
const { value1, value2, op } = req.body;
1015+
1016+
// validate the data here
1017+
1018+
// assert the type
1019+
const operation = op as Operation; // highlight-line
1020+
1021+
const result = calculator(Number(value1), Number(value2), operation); // highlight-line
1022+
1023+
return res.send({ result });
1024+
});
1025+
```
1026+
1027+
The defined constant _operation_ has now the type _Operation_ and the compiler is perfectly happy, no quieting of the Eslint rule is needed on the following function call. The new variable is actually not needed, the type assertion can be done when an argument is passed to the function:
1028+
1029+
```js
1030+
app.post('/calculate', (req, res) => {
1031+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1032+
const { value1, value2, op } = req.body;
1033+
1034+
// validate the data here
1035+
1036+
const result = calculator(
1037+
Number(value1), Number(value2), op as Operation // highlight-line
1038+
);
1039+
1040+
return res.send({ result });
1041+
});
1042+
```
1043+
1044+
Using a type assertion (or quieting an Eslint rule) is always a bit risky thing. It leaves the TypeScript compiler off the hook, the compiler just trusts that we as developers know what we are doing. If the asserted type does <i>not</i> have the right kind of value, the result will be a runtime error, so one must be pretty careful when validating the data if a type assertion is used.
1045+
1046+
In the next chapter we shall have a look at [type narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) which will provide a much more safe way of giving a stricter type for data that is coming from an external source.
1047+
9911048
</div>
9921049
9931050
<div class="tasks">

0 commit comments

Comments
 (0)