diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json index 9850457281e..860f1d5563a 100644 --- a/client/i18n/locales/english/intro.json +++ b/client/i18n/locales/english/intro.json @@ -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": [ diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b4e284291adcfcb90df47.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b4e284291adcfcb90df47.md new file mode 100644 index 00000000000..6b122b7774b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b4e284291adcfcb90df47.md @@ -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-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b5370a43e299b4673bcaa.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b5370a43e299b4673bcaa.md new file mode 100644 index 00000000000..e1c69e4c026 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b5370a43e299b4673bcaa.md @@ -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-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b58c5abecfda9dc81c4ec.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b58c5abecfda9dc81c4ec.md new file mode 100644 index 00000000000..1a91a3f9927 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b58c5abecfda9dc81c4ec.md @@ -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); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b5b7cb68b6d35afca61b7.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b5b7cb68b6d35afca61b7.md new file mode 100644 index 00000000000..a874eaf3510 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b5b7cb68b6d35afca61b7.md @@ -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); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b61ca8c9cb9bc1968364e.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b61ca8c9cb9bc1968364e.md new file mode 100644 index 00000000000..3f7cd12dd0a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b61ca8c9cb9bc1968364e.md @@ -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); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b62c99ec8c63470313365.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b62c99ec8c63470313365.md new file mode 100644 index 00000000000..329c1993dd7 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b62c99ec8c63470313365.md @@ -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); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b64096301fd75e3efeb1b.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b64096301fd75e3efeb1b.md new file mode 100644 index 00000000000..ab9dfe7b8dc --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b64096301fd75e3efeb1b.md @@ -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); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b65caf17a8e8d340af27d.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b65caf17a8e8d340af27d.md new file mode 100644 index 00000000000..af01bdfb0eb --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b65caf17a8e8d340af27d.md @@ -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); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b68f94a0d154c2a7a5ce0.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b68f94a0d154c2a7a5ce0.md new file mode 100644 index 00000000000..85973ad83a2 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b68f94a0d154c2a7a5ce0.md @@ -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-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b6d91cde15459d3619c4a.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b6d91cde15459d3619c4a.md new file mode 100644 index 00000000000..aa66b73d7ce --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b6d91cde15459d3619c4a.md @@ -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-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b6dd1a2961bd38491ef2e.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b6dd1a2961bd38491ef2e.md new file mode 100644 index 00000000000..2f45dee15a2 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b6dd1a2961bd38491ef2e.md @@ -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 = { + 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 `` 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`. + +```js +const explorer = await __helpers.Explorer(code); +const { userRoles } = explorer.variables; +assert.isTrue(userRoles.annotation.matches("Record")); +``` + +# --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); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b70b10ae344c802d106b0.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b70b10ae344c802d106b0.md new file mode 100644 index 00000000000..ca218f95b11 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b70b10ae344c802d106b0.md @@ -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 = { + admin: "full-access", + editor: "limited-access", + viewer: "read-only" + +}; +--fcc-editable-region-- + +console.log(userRoles); +``` diff --git a/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b71d98c820ac7ab9eea2a.md b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b71d98c820ac7ab9eea2a.md new file mode 100644 index 00000000000..35e306b9c5a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-type-safe-user-profile/699b71d98c820ac7ab9eea2a.md @@ -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 = { + 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 = { + admin: "full-access", + editor: "limited-access", + viewer: "read-only", + moderator: "medium-access", + guest: "read-only" +}; + +console.log(userRoles); +``` diff --git a/curriculum/structure/blocks/workshop-type-safe-user-profile.json b/curriculum/structure/blocks/workshop-type-safe-user-profile.json new file mode 100644 index 00000000000..ce781fb536e --- /dev/null +++ b/curriculum/structure/blocks/workshop-type-safe-user-profile.json @@ -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 +} diff --git a/curriculum/structure/superblocks/front-end-development-libraries-v9.json b/curriculum/structure/superblocks/front-end-development-libraries-v9.json index 042b024391d..20793aec875 100644 --- a/curriculum/structure/superblocks/front-end-development-libraries-v9.json +++ b/curriculum/structure/superblocks/front-end-development-libraries-v9.json @@ -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",