mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(curriculum): Add type safe user profile to typescript module (#66005)
Co-authored-by: Sem Bauke <sem@freecodecamp.org> Co-authored-by: Dario <105294544+Dario-DC@users.noreply.github.com> Co-authored-by: Kolade <chrisjay967@gmail.com>
This commit is contained in:
@@ -6181,6 +6181,12 @@
|
||||
"In these lessons, you will learn what TypeScript is and how to use it."
|
||||
]
|
||||
},
|
||||
"workshop-type-safe-user-profile": {
|
||||
"title": "Build a Type Safe User Profile",
|
||||
"intro": [
|
||||
"In this workshop, you will practice working with type annotations, array types, object types and more by building out a user profile."
|
||||
]
|
||||
},
|
||||
"lecture-understanding-type-composition": {
|
||||
"title": "Understanding Type Composition",
|
||||
"intro": [
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
---
|
||||
id: 699b4e284291adcfcb90df47
|
||||
title: Step 1
|
||||
challengeType: 1
|
||||
dashedName: step-1
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In this workshop, you will practice working with primitive types as well as object and array types by building out a type safe user profile.
|
||||
|
||||
Start by creating a variable called `profile` and assign it an object. Your object should have these three properties and values:
|
||||
|
||||
```md
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a variable called `profile`.
|
||||
|
||||
```js
|
||||
assert.exists(profile);
|
||||
```
|
||||
|
||||
Your `profile` variable should be an object.
|
||||
|
||||
```js
|
||||
assert.isObject(profile);
|
||||
```
|
||||
|
||||
You should have a `username` property in your `profile` object.
|
||||
|
||||
```js
|
||||
assert.property(profile, "username");
|
||||
```
|
||||
|
||||
Your `username` property should have a value of `"codeLearner"`.
|
||||
|
||||
```js
|
||||
assert.propertyVal(profile, "username", "codeLearner");
|
||||
```
|
||||
|
||||
Your `profile` object should have an `age` property.
|
||||
|
||||
```js
|
||||
assert.property(profile, "age");
|
||||
```
|
||||
|
||||
Your `age` property should have a value of `25`.
|
||||
|
||||
```js
|
||||
assert.propertyVal(profile, "age", 25);
|
||||
```
|
||||
|
||||
Your `profile` object should have an `isLoggedIn` property.
|
||||
|
||||
```js
|
||||
assert.property(profile, "isLoggedIn");
|
||||
```
|
||||
|
||||
Your `isLoggedIn` property should have a value of `false`.
|
||||
|
||||
```js
|
||||
assert.propertyVal(profile, "isLoggedIn", false);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: 699b5370a43e299b4673bcaa
|
||||
title: Step 2
|
||||
challengeType: 1
|
||||
dashedName: step-2
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
It would be helpful to see the `profile` object properties in the console as you build it out.
|
||||
|
||||
Add a `console.log` that logs `profile` to the console.
|
||||
|
||||
# --before-each--
|
||||
|
||||
```js
|
||||
const spy = __helpers.spyOn(console, 'log');
|
||||
const getLogs = () => spy.calls.map(call => call?.[0]);
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
You should log `profile` to the console.
|
||||
|
||||
```js
|
||||
assert.equal(getLogs()[0], profile);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: 699b58c5abecfda9dc81c4ec
|
||||
title: Step 3
|
||||
challengeType: 1
|
||||
dashedName: step-3
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Right now the `profile` object only has three properties. But it would be nice to have a few more.
|
||||
|
||||
Add a property called `mood` to the `profile` object. Its value should be `null`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `profile` object should have a `mood` property.
|
||||
|
||||
```js
|
||||
assert.property(profile, "mood");
|
||||
```
|
||||
|
||||
Your `mood` property should have a value of `null`.
|
||||
|
||||
```js
|
||||
assert.isNull(profile?.mood);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
--fcc-editable-region--
|
||||
const profile = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
console.log(profile);
|
||||
```
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
---
|
||||
id: 699b5b7cb68b6d35afca61b7
|
||||
title: Step 4
|
||||
challengeType: 1
|
||||
dashedName: step-4
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
While it is possible to continue to add properties like this to the `profile` object, it would be nice to restrict the types of properties that should be allowed. It would be better to define the shape of the `profile` object beforehand.
|
||||
|
||||
In prior lessons, you learned how to work with object types like this:
|
||||
|
||||
```ts
|
||||
const composer: {
|
||||
name: string;
|
||||
instrument: string;
|
||||
isActive: boolean;
|
||||
} = {
|
||||
name: "Ludwig van Beethoven",
|
||||
instrument: "Piano",
|
||||
isActive: false
|
||||
};
|
||||
```
|
||||
|
||||
In this example, `composer` is an object with an inline object type. This object has three required properties of `name`, `instrument` and `isActive`. The first two properties must be of type `string` and `isActive` must be of type `boolean`.
|
||||
|
||||
Update your existing `profile` object to include an inline object type. `username` should be typed to a `string`, `age` should be typed to a `number` and `isLoggedIn` should be typed to a `boolean`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `profile` object should have an inline object type with a `username` property set to `string`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { profile } = explorer.variables;
|
||||
const prop = { name: "username", type: "string" };
|
||||
assert.isTrue(profile.annotation.hasTypeProps(prop));
|
||||
```
|
||||
|
||||
Your `profile` object should have an inline object type with an `age` property set to `number`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { profile } = explorer.variables;
|
||||
const prop = { name: "age", type: "number" };
|
||||
assert.isTrue(profile.annotation.hasTypeProps(prop));
|
||||
```
|
||||
|
||||
Your `profile` object should have an inline object type with an `isLoggedIn` property set to `boolean`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { profile } = explorer.variables;
|
||||
const prop = { name: "isLoggedIn", type: "boolean" };
|
||||
assert.isTrue(profile.annotation.hasTypeProps(prop));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
--fcc-editable-region--
|
||||
const profile = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
mood: null
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
console.log(profile);
|
||||
```
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
---
|
||||
id: 699b61ca8c9cb9bc1968364e
|
||||
title: Step 5
|
||||
challengeType: 1
|
||||
dashedName: step-5
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
If you open up the console, you should see the following error message:
|
||||
|
||||
```md
|
||||
Object literal may only specify known properties, and 'mood' does not exist in type '{ username: string; age: number; isLoggedIn: boolean; }'.
|
||||
```
|
||||
|
||||
In vanilla JavaScript, you were allowed to add whatever properties and values you liked to the `profile` object. But in TypeScript, the compiler checks that the object only has the properties you defined (`username`, `age`, and `isLoggedIn` in this case). Since `mood` wasn't included in the type, TypeScript treats it as an invalid property and throws an error.
|
||||
|
||||
Remove the `mood: null` from your code.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should no longer have `mood: null` in your code.
|
||||
|
||||
```js
|
||||
assert.isUndefined(profile.mood);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false
|
||||
--fcc-editable-region--
|
||||
mood: null
|
||||
--fcc-editable-region--
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
```
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: 699b62c99ec8c63470313365
|
||||
title: Step 6
|
||||
challengeType: 1
|
||||
dashedName: step-6
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now it is time to add another property to your `profile` object.
|
||||
|
||||
Start by updating the inline object type to include a `bio` property with a type set to `string`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should update the inline object type to include a `bio` property set to `string`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { profile } = explorer.variables;
|
||||
const prop = { name: "bio", type: "string" };
|
||||
assert.isTrue(profile.annotation.hasTypeProps(prop));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
--fcc-editable-region--
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
|
||||
--fcc-editable-region--
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
```
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: 699b64096301fd75e3efeb1b
|
||||
title: Step 7
|
||||
challengeType: 1
|
||||
dashedName: step-7
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
If you open the console again, you will see a different error message:
|
||||
|
||||
```md
|
||||
Property 'bio' is missing in type '{ username: string; age: number; isLoggedIn: false; }' but required in type '{ username: string; age: number; isLoggedIn: boolean; bio: string; }'.
|
||||
```
|
||||
|
||||
As you recall in prior lessons, you can mark properties as optional by using the `?` symbol next to the property name like this:
|
||||
|
||||
```ts
|
||||
const musician: {
|
||||
name: string;
|
||||
instrument: string;
|
||||
isActive: boolean;
|
||||
numberOfAlbums?: number;
|
||||
} = {
|
||||
name: "Ludwig van Beethoven",
|
||||
instrument: "Piano",
|
||||
isActive: false
|
||||
};
|
||||
```
|
||||
|
||||
In this example, `numberOfAlbums` is marked optional so TypeScript will not throw an error in this case.
|
||||
|
||||
Update your `bio` property to be marked as optional.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `bio` property should be marked as optional.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { profile } = explorer.variables;
|
||||
const prop = { name: "bio", type: "string", isOptional: true };
|
||||
assert.isTrue(profile.annotation.hasTypeProps(prop));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
--fcc-editable-region--
|
||||
bio: string
|
||||
--fcc-editable-region--
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
```
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: 699b65caf17a8e8d340af27d
|
||||
title: Step 8
|
||||
challengeType: 1
|
||||
dashedName: step-8
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now it is time to add the last property to the `profile` object.
|
||||
|
||||
In prior lessons, you learned how to work with array types. You learned about different array types like `string[]`, or `number[]`:
|
||||
|
||||
```ts
|
||||
const scores: number[] = [95, 87, 100];
|
||||
```
|
||||
|
||||
In this example, the `scores` array only accepts numbers. If you try to add another type like a `string`, or `null` then TypeScript will throw an error.
|
||||
|
||||
Update your `profile` object to add a `programmingLanguages` property, typed as a `string[]`. Inside the object literal, set this property to `["JavaScript", "Python", "C++"]`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a `programmingLanguages` property inside of your `profile` object.
|
||||
|
||||
```js
|
||||
assert.property(profile, "programmingLanguages");
|
||||
```
|
||||
|
||||
Your `programmingLanguages` property should have the value of `["JavaScript", "Python", "C++"]`.
|
||||
|
||||
```js
|
||||
assert.deepEqual(profile.programmingLanguages, ["JavaScript", "Python", "C++"]);
|
||||
```
|
||||
|
||||
Your `programmingLanguages` property should be typed as a `string[]`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { profile } = explorer.variables;
|
||||
const prop = { name: "programmingLanguages", type: "string[]" };
|
||||
assert.isTrue(profile.annotation.hasTypeProps(prop));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
--fcc-editable-region--
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
bio?: string;
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false
|
||||
};
|
||||
--fcc-editable-region--
|
||||
|
||||
console.log(profile);
|
||||
```
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
---
|
||||
id: 699b68f94a0d154c2a7a5ce0
|
||||
title: Step 9
|
||||
challengeType: 1
|
||||
dashedName: step-9
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that the `profile` object is complete, it is time to build out a new object which represents the user roles.
|
||||
|
||||
Create a new variable called `userRoles` and assign it an object. Your object should have the following properties and values:
|
||||
|
||||
```md
|
||||
admin: "full-access",
|
||||
editor: "limited-access",
|
||||
viewer: "read-only"
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a variable called `userRoles`.
|
||||
|
||||
```js
|
||||
assert.isDefined(userRoles);
|
||||
```
|
||||
|
||||
Your `userRoles` variable should be an object.
|
||||
|
||||
```js
|
||||
assert.isObject(userRoles);
|
||||
```
|
||||
|
||||
Your `userRoles` object should have an `admin` property.
|
||||
|
||||
```js
|
||||
assert.property(userRoles, "admin");
|
||||
```
|
||||
|
||||
Your `admin` property should have a value of `"full-access"`.
|
||||
|
||||
```js
|
||||
assert.propertyVal(userRoles, "admin", "full-access");
|
||||
```
|
||||
|
||||
Your `userRoles` object should have an `editor` property.
|
||||
|
||||
```js
|
||||
assert.property(userRoles, "editor");
|
||||
```
|
||||
|
||||
Your `editor` property should have a value of `"limited-access"`.
|
||||
|
||||
```js
|
||||
assert.propertyVal(userRoles, "editor", "limited-access");
|
||||
```
|
||||
|
||||
Your `userRoles` object should have a `viewer` property.
|
||||
|
||||
```js
|
||||
assert.property(userRoles, "viewer");
|
||||
```
|
||||
|
||||
Your `viewer` property should have a value of `"read-only"`.
|
||||
|
||||
```js
|
||||
assert.propertyVal(userRoles, "viewer", "read-only");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
bio?: string;
|
||||
programmingLanguages: string[];
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
programmingLanguages: ["JavaScript", "Python", "C++"]
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
---
|
||||
id: 699b6d91cde15459d3619c4a
|
||||
title: Step 10
|
||||
challengeType: 1
|
||||
dashedName: step-10
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Just like the `profile` object, it would be nice to see the `userRoles` object in the console.
|
||||
|
||||
Add a `console.log()` to log the `userRoles` object.
|
||||
|
||||
# --before-each--
|
||||
|
||||
```js
|
||||
const spy = __helpers.spyOn(console, 'log');
|
||||
const getLogs = () => spy.calls.map(call => call?.[0]);
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
You should log `userRoles` to the console.
|
||||
|
||||
```js
|
||||
assert.equal(getLogs()[1], userRoles);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
bio?: string;
|
||||
programmingLanguages: string[];
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
programmingLanguages: ["JavaScript", "Python", "C++"]
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
|
||||
const userRoles = {
|
||||
admin: "full-access",
|
||||
editor: "limited-access",
|
||||
viewer: "read-only"
|
||||
};
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
---
|
||||
id: 699b6dd1a2961bd38491ef2e
|
||||
title: Step 11
|
||||
challengeType: 1
|
||||
dashedName: step-11
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `profile` object had strict keys and types. If you tried to add a property that wasn't defined, or assign an incompatible type, TypeScript would throw an error.
|
||||
|
||||
It would be nice if the `userRoles` object wasn't as strict and had more flexible keys.
|
||||
|
||||
As you recall in the prior lessons, you can achieve this result by using the `Record` type:
|
||||
|
||||
```ts
|
||||
const studentGrades: Record<string, number> = {
|
||||
Math: 95,
|
||||
English: 88,
|
||||
Science: 92
|
||||
};
|
||||
```
|
||||
|
||||
In this `studentGrades` example, TypeScript doesn't need to know all of the subjects upfront. This allows you to add more subjects like `Art` or `Music` later on. TypeScript only cares that the values for each key is a number.
|
||||
|
||||
Update your `userRoles` object by adding an inline `Record` type. Set it to `<string, string>` so that each key can be any string, and each value must be a string.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `userRoles` object should have an inline `Record` type set to `Record<string, string>`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { userRoles } = explorer.variables;
|
||||
assert.isTrue(userRoles.annotation.matches("Record<string, string>"));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
bio?: string;
|
||||
programmingLanguages: string[];
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
programmingLanguages: ["JavaScript", "Python", "C++"]
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
|
||||
--fcc-editable-region--
|
||||
const userRoles = {
|
||||
--fcc-editable-region--
|
||||
admin: "full-access",
|
||||
editor: "limited-access",
|
||||
viewer: "read-only"
|
||||
};
|
||||
|
||||
console.log(userRoles);
|
||||
```
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
---
|
||||
id: 699b70b10ae344c802d106b0
|
||||
title: Step 12
|
||||
challengeType: 1
|
||||
dashedName: step-12
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now it is time to add a couple more properties to the `userRoles` object.
|
||||
|
||||
Add a `moderator` property with a value of `"medium-access"`.
|
||||
|
||||
Then below that, add a `guest` property with a value of `3`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `userRoles` object should have a `moderator` property.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { userRoles } = explorer.variables;
|
||||
assert.exists(userRoles.objectProps.moderator);
|
||||
```
|
||||
|
||||
Your `moderator` property should have a value of `"medium-access"`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { userRoles } = explorer.variables;
|
||||
assert.isTrue(userRoles.objectProps.moderator.value.matches("'medium-access'"));
|
||||
```
|
||||
|
||||
Your `userRoles` object should have a `guest` property.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { userRoles } = explorer.variables;
|
||||
assert.exists(userRoles.objectProps.guest);
|
||||
```
|
||||
|
||||
Your `guest` property should have a value of the number `3`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { userRoles } = explorer.variables;
|
||||
assert.isTrue(userRoles.objectProps.guest.value.matches("3"));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
bio?: string;
|
||||
programmingLanguages: string[];
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
programmingLanguages: ["JavaScript", "Python", "C++"]
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
|
||||
--fcc-editable-region--
|
||||
const userRoles: Record<string, string> = {
|
||||
admin: "full-access",
|
||||
editor: "limited-access",
|
||||
viewer: "read-only"
|
||||
|
||||
};
|
||||
--fcc-editable-region--
|
||||
|
||||
console.log(userRoles);
|
||||
```
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: 699b71d98c820ac7ab9eea2a
|
||||
title: Step 13
|
||||
challengeType: 1
|
||||
dashedName: step-13
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
If you open the console again, you will see a new error message:
|
||||
|
||||
```md
|
||||
Type 'number' is not assignable to type 'string'.
|
||||
```
|
||||
|
||||
This is happening because you are attempting to add a `guest` property with a number value.
|
||||
|
||||
To remove the TypeScript error, change the `guest` property value to `"read-only"`.
|
||||
|
||||
With that last change, your user profile workshop is complete!
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `guest` property for the `userRoles` object should have the value of `"read-only"`.
|
||||
|
||||
```js
|
||||
assert.propertyVal(userRoles, "guest", "read-only");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
bio?: string;
|
||||
programmingLanguages: string[];
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
programmingLanguages: ["JavaScript", "Python", "C++"]
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
|
||||
const userRoles: Record<string, string> = {
|
||||
admin: "full-access",
|
||||
editor: "limited-access",
|
||||
viewer: "read-only",
|
||||
moderator: "medium-access",
|
||||
--fcc-editable-region--
|
||||
guest: 3
|
||||
--fcc-editable-region--
|
||||
};
|
||||
|
||||
console.log(userRoles);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```ts
|
||||
const profile: {
|
||||
username: string;
|
||||
age: number;
|
||||
isLoggedIn: boolean;
|
||||
bio?: string;
|
||||
programmingLanguages: string[];
|
||||
} = {
|
||||
username: "codeLearner",
|
||||
age: 25,
|
||||
isLoggedIn: false,
|
||||
programmingLanguages: ["JavaScript", "Python", "C++"]
|
||||
};
|
||||
|
||||
console.log(profile);
|
||||
|
||||
const userRoles: Record<string, string> = {
|
||||
admin: "full-access",
|
||||
editor: "limited-access",
|
||||
viewer: "read-only",
|
||||
moderator: "medium-access",
|
||||
guest: "read-only"
|
||||
};
|
||||
|
||||
console.log(userRoles);
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"isUpcomingChange": true,
|
||||
"dashedName": "workshop-type-safe-user-profile",
|
||||
"helpCategory": "JavaScript",
|
||||
"blockLayout": "challenge-grid",
|
||||
"challengeOrder": [
|
||||
{ "id": "699b4e284291adcfcb90df47", "title": "Step 1" },
|
||||
{ "id": "699b5370a43e299b4673bcaa", "title": "Step 2" },
|
||||
{ "id": "699b58c5abecfda9dc81c4ec", "title": "Step 3" },
|
||||
{ "id": "699b5b7cb68b6d35afca61b7", "title": "Step 4" },
|
||||
{ "id": "699b61ca8c9cb9bc1968364e", "title": "Step 5" },
|
||||
{ "id": "699b62c99ec8c63470313365", "title": "Step 6" },
|
||||
{ "id": "699b64096301fd75e3efeb1b", "title": "Step 7" },
|
||||
{ "id": "699b65caf17a8e8d340af27d", "title": "Step 8" },
|
||||
{ "id": "699b68f94a0d154c2a7a5ce0", "title": "Step 9" },
|
||||
{ "id": "699b6d91cde15459d3619c4a", "title": "Step 10" },
|
||||
{ "id": "699b6dd1a2961bd38491ef2e", "title": "Step 11" },
|
||||
{ "id": "699b70b10ae344c802d106b0", "title": "Step 12" },
|
||||
{ "id": "699b71d98c820ac7ab9eea2a", "title": "Step 13" }
|
||||
],
|
||||
"blockLabel": "workshop",
|
||||
"usesMultifileEditor": true,
|
||||
"hasEditableBoundaries": true
|
||||
}
|
||||
@@ -91,6 +91,7 @@
|
||||
"comingSoon": true,
|
||||
"blocks": [
|
||||
"lecture-introduction-to-typescript",
|
||||
"workshop-type-safe-user-profile",
|
||||
"lecture-understanding-type-composition",
|
||||
"lecture-working-with-generics-and-type-narrowing",
|
||||
"lecture-working-with-typescript-configuration-files",
|
||||
|
||||
Reference in New Issue
Block a user