You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
updateStatus(albumAttributes);// red squiggly line under albumAttributes
139
+
updateStatus(albumAttributes);
135
140
```
136
141
137
-
TypeScript gives us an error below `albumAttributes` inside of the `updateStatus` function call, with messages similar to what we saw before:
138
-
139
-
```
140
-
Argument of type '{ status: string; }' is not assignable to parameter of type 'AlbumAttributes'.
141
-
Types of property 'status' are incompatible.
142
-
Type 'string' is not assignable to type '"new-release" | "on-sale" | "staff-pick"'.
143
-
```
142
+
TypeScript gives us an error below `albumAttributes` inside of the `updateStatus` function call, with messages similar to what we saw before.
144
143
145
144
This is happening because TypeScript has inferred the `status` property as a `string` rather than the specific literal type `"on-sale"`. Similar to with `let`, TypeScript understands that the property could later be reassigned:
Note that like many of TypeScript's type helpers, the immutability enforced by `Readonly` only operates on the first level. It won't make properties read-only recursively.
Note that, just like the `readonly` for object properties, this doesn't affect the runtime behavior of the array. It's just a way to help catch potential errors.
@@ -304,14 +321,21 @@ However, the reverse is not true.
304
321
305
322
If we declare a read-only array, we can only pass it to `printGenresReadOnly`. Attempting to pass it to `printGenresMutable` will yield an error:
306
323
307
-
```typescript
324
+
```ts twoslash
325
+
// @errors: 2345
326
+
function printGenresReadOnly(genres:readonlystring[]) {
printGenresMutable(readOnlyGenres); // red squiggly line under readOnlyGenres
312
-
313
-
// hovering over readOnlyGenres shows:
314
-
// Error: Argument of type 'readonly ["rock", "pop", "unclassifiable"]' is not assignable to parameter of type 'string[]'
338
+
printGenresMutable(readOnlyGenres);
315
339
```
316
340
317
341
This is because we might be mutating the array inside of `printGenresMutable`. If we passed a read-only array.
@@ -328,7 +352,8 @@ Here we have a `modifyButtons` function that takes in an array of objects with `
328
352
329
353
When attempting to call `modifyButtons` with an array of objects that seem to meet the contract, TypeScript gives us an error:
330
354
331
-
```typescript
355
+
```ts twoslash
356
+
// @errors: 2345
332
357
typeButtonAttributes= {
333
358
type:"button"|"submit"|"reset";
334
359
};
@@ -344,7 +369,7 @@ const buttonsToChange = [
344
369
},
345
370
];
346
371
347
-
modifyButtons(buttonsToChange);// red squiggly line under buttonsToChange
372
+
modifyButtons(buttonsToChange);
348
373
```
349
374
350
375
Your task is to determine why this error shows up, then resolve it.
@@ -353,16 +378,17 @@ Your task is to determine why this error shows up, then resolve it.
353
378
354
379
This `printNames` function accepts an array of `name` strings and logs them to the console. However, there are also non-working `@ts-expect-error` comments that should not allow for names to be added or changed:
355
380
356
-
```typescript
381
+
```ts twoslash
382
+
// @errors: 2578
357
383
function printNames(names:string[]) {
358
384
for (const name ofnames) {
359
385
console.log(name);
360
386
}
361
387
362
-
// @ts-expect-error // red squiggly line
388
+
// @ts-expect-error
363
389
names.push("John");
364
390
365
-
// @ts-expect-error // red squiggly line
391
+
// @ts-expect-error
366
392
names[0] ="Billy";
367
393
}
368
394
```
@@ -393,9 +419,19 @@ Given that `pop` removes the last element from an array, calling `dangerousFunct
393
419
394
420
Currently, TypeScript does not alert us to this potential issue, as seen by the error line under `@ts-expect-error`:
// Argument of type 'Coordinate' is not assignable to parameter of type 'number[]'.
520
-
// The type 'Coordinate' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
554
+
dangerousFunction(myHouse);
521
555
```
522
556
523
557
We get an error because the function's signature expects a modifiable array of numbers, but `myHouse` is a read-only tuple. TypeScript is protecting us against unwanted changes.
@@ -562,8 +596,14 @@ The `as const` assertion has made the entire object deeply read-only, including
562
596
563
597
Attempting to change the `status` property will result in an error:
564
598
565
-
```typescript
566
-
albumAttributes.status="new-release"; // red squiggly line under status
599
+
```ts twoslash
600
+
// @errors: 2540
601
+
const albumAttributes = {
602
+
status: "on-sale",
603
+
} asconst;
604
+
605
+
// ---cut---
606
+
albumAttributes.status="new-release";
567
607
```
568
608
569
609
This makes `as const` ideal for large config objects that you don't expect to change.
Recall that the `Readonly` modifier only works on the _first level_ of an object. If we try to add a new `backWall` property to the `shelfLocations` object, TypeScript will throw an error:
674
+
Recall that the `Readonly` modifier only works on the _first level_ of an object. If we try to modify the `frontCounter` property, TypeScript will throw an error:
635
675
636
-
```typescript
637
-
shelfLocations.backWall= { status: "on-sale" }; // red squiggly line under backWall
676
+
```ts twoslash
677
+
// @errors: 2540
678
+
const shelfLocations =Object.freeze({
679
+
entrance: {
680
+
status: "on-sale",
681
+
},
682
+
frontCounter: {
683
+
status: "staff-pick",
684
+
},
685
+
endCap: {
686
+
status: "new-release",
687
+
},
688
+
});
638
689
639
-
// hovering over backWall shows:
640
-
// Property 'backWall' does not exist on type 'Readonly<{ entrance: { status: string; }; frontCounter: { status: string; }; endCap: { status: string; }; }>'
690
+
// ---cut---
691
+
shelfLocations.frontCounter= {
692
+
status: "new-release",
693
+
};
641
694
```
642
695
643
696
However, we are able to change the nested `status` property of a specific location:
@@ -650,7 +703,7 @@ This is in line with how `Object.freeze` works in JavaScript. It only makes the
650
703
651
704
Using `as const` makes the entire object deeply read-only, including all nested properties:
652
705
653
-
```typescript
706
+
```ts twoslash
654
707
const shelfLocations = {
655
708
entrance: {
656
709
status: "on-sale",
@@ -663,18 +716,8 @@ const shelfLocations = {
663
716
},
664
717
} asconst;
665
718
666
-
// hovering over shelfLocations shows:
667
-
const shelfLocations: {
668
-
readonly entrance: {
669
-
readonly status:"on-sale";
670
-
};
671
-
readonly frontCounter: {
672
-
readonly status:"staff-pick";
673
-
};
674
-
readonly endCap: {
675
-
readonly status:"new-release";
676
-
};
677
-
};
719
+
console.log(shelfLocations);
720
+
// ^?
678
721
```
679
722
680
723
Of course, this is just a type-level annotation. `Object.freeze` gives you runtime immutability, while `as const` gives you type-level immutability. I actually prefer the latter - doing less work at runtime is always a good thing.
0 commit comments