From 968f231733fa40cf730e6fa66039e970cc16d5e1 Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Fri, 6 Dec 2019 20:04:40 -0800 Subject: [PATCH 1/3] Wordsmith --- src/pages/todo.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx index 4286142..b3f6544 100644 --- a/src/pages/todo.tsx +++ b/src/pages/todo.tsx @@ -858,7 +858,9 @@ const Page = () => ( TypeScript’s types act as lightweight unit tests that run automatically every time you save (compile) the code. {' '} - It helps you write less buggy code with very little overhead. + It helps you write less buggy code with very little overhead. (Of + course, this analogy is a simplification and you should still + write tests in TypeScript!)

This especially useful{' '} @@ -872,7 +874,9 @@ const Page = () => (

TypeScript reduces bugs when transforming data} + description={ + <>TypeScript reduces bugs when transforming/passing data + } />

From 76defc0b232f2fb9112301bb1f16bdeebaf06b9d Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Fri, 6 Dec 2019 21:59:44 -0800 Subject: [PATCH 2/3] Start mark all as complete --- snippets/snippets/todo/tdbp.ts | 5 +++ src/lib/snippets.ts | 6 ++++ src/pages/todo.tsx | 63 ++++++++++++++++++++++------------ 3 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 snippets/snippets/todo/tdbp.ts diff --git a/snippets/snippets/todo/tdbp.ts b/snippets/snippets/todo/tdbp.ts new file mode 100644 index 0000000..8abd4e1 --- /dev/null +++ b/snippets/snippets/todo/tdbp.ts @@ -0,0 +1,5 @@ +// Takes an array of todo items and returns +// a new array where "done" is all true +function completeAll(todos) { + // ... +} diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts index 36ac729..f6ffd96 100644 --- a/src/lib/snippets.ts +++ b/src/lib/snippets.ts @@ -605,6 +605,12 @@ export const reel = `function toggleTodo(todo) { } }` +export const tdbp = `// Takes an array of todo items and returns +// a new array where "done" is all true +function completeAll(todos) { + // ... +}` + export const tgvw = `const bar: Todo = { text: '…', done: true diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx index b3f6544..119c387 100644 --- a/src/pages/todo.tsx +++ b/src/pages/todo.tsx @@ -859,8 +859,8 @@ const Page = () => ( automatically every time you save (compile) the code. {' '} It helps you write less buggy code with very little overhead. (Of - course, this analogy is a simplification and you should still - write tests in TypeScript!) + course, this analogy is a simplification. You should still write + tests in TypeScript!)

This especially useful{' '} @@ -897,26 +897,45 @@ const Page = () => ( ) }, - // { - // title: <>Mark all as completed, - // content: ( - // <> - // - // ↓ Try pressing “Mark all as completed” - // - // } - // defaultData={[ - // { id: 1, text: 'First todo', done: false }, - // { id: 2, text: 'Second todo', done: false } - // ]} - // showMarkAllAsCompleted - // /> - // - // ) - // }, + { + title: <>Mark all as completed, + content: ( + <> +

+ Some todo apps allow you to{' '} + mark all items as completed. On the + following todo app,{' '} + try pressing “Mark all as completed”: +

+ + ↓ Try pressing “Mark all as completed” + + } + defaultData={[ + { id: 1, text: 'First todo', done: false }, + { id: 2, text: 'Second todo', done: false } + ]} + showMarkAllAsCompleted + highlightLineIndexOffset={1} + shouldHighlight={tokenIndex => tokenIndex === 15} + /> +

+ After pressing “Mark all as completed”, all items will have{' '} + done: true. +

+

+ Let’s implement this functionality using TypeScript. We’ll write a + function called completeAll() which takes an array of + todo items and returns a new array where done is all{' '} + true. +

+ + + ) + }, underConstructionCard ]} /> From 30a80dba1b028a9deb85f913191eec9ac9815569 Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Sat, 7 Dec 2019 00:18:22 -0800 Subject: [PATCH 3/3] Continue with mark all as completed --- snippets/snippets/todo/lgci.ts | 11 +++ snippets/snippets/todo/mnmy.ts | 4 + snippets/snippets/todo/mwrj.ts | 8 ++ snippets/snippets/todo/szan.ts | 6 ++ src/components/CodeBlock.tsx | 21 ++--- src/lib/snippets.ts | 33 ++++++++ src/pages/todo.tsx | 147 +++++++++++++++++++++++++++++---- 7 files changed, 204 insertions(+), 26 deletions(-) create mode 100644 snippets/snippets/todo/lgci.ts create mode 100644 snippets/snippets/todo/mnmy.ts create mode 100644 snippets/snippets/todo/mwrj.ts create mode 100644 snippets/snippets/todo/szan.ts diff --git a/snippets/snippets/todo/lgci.ts b/snippets/snippets/todo/lgci.ts new file mode 100644 index 0000000..7b38bfe --- /dev/null +++ b/snippets/snippets/todo/lgci.ts @@ -0,0 +1,11 @@ +// Same as before +type Todo = Readonly<{ + id: number + text: string + done: boolean +}> + +// Input is an array of Todo items: Todo[] +function completeAll(todos: Todo[]) { + // ... +} diff --git a/snippets/snippets/todo/mnmy.ts b/snippets/snippets/todo/mnmy.ts new file mode 100644 index 0000000..d72cfe2 --- /dev/null +++ b/snippets/snippets/todo/mnmy.ts @@ -0,0 +1,4 @@ +// Output is an array of Todo items: Todo[] +function completeAll(todos: Todo[]): Todo[] { + // ... +} diff --git a/snippets/snippets/todo/mwrj.ts b/snippets/snippets/todo/mwrj.ts new file mode 100644 index 0000000..5fbb963 --- /dev/null +++ b/snippets/snippets/todo/mwrj.ts @@ -0,0 +1,8 @@ +// After declaring todos as: readonly Todo[], +// the following code WILL NOT compile: + +// Compile error - modifies the array +todos[0] = { id: 1, text: '…', done: true } + +// Compile error - push() modifies the array +todos.push({ id: 1, text: '…', done: true }) diff --git a/snippets/snippets/todo/szan.ts b/snippets/snippets/todo/szan.ts new file mode 100644 index 0000000..140d75b --- /dev/null +++ b/snippets/snippets/todo/szan.ts @@ -0,0 +1,6 @@ +// Make input todos as readonly array +function completeAll( + todos: readonly Todo[] +): Todo[] { + // ... +} diff --git a/src/components/CodeBlock.tsx b/src/components/CodeBlock.tsx index 12c803e..2c9ab5f 100644 --- a/src/components/CodeBlock.tsx +++ b/src/components/CodeBlock.tsx @@ -20,7 +20,8 @@ const CodeBlock = ({ compile, shouldHighlightResult, resultError, - tokenIndexIndentWorkaround + tokenIndexIndentWorkaround, + defaultErrorHighlight }: { snippet: string shouldHighlight?: (lineIndex: number, tokenIndex: number) => boolean @@ -33,6 +34,7 @@ const CodeBlock = ({ shouldHighlightResult?: (lineIndex: number, tokenIndex: number) => boolean resultError?: boolean tokenIndexIndentWorkaround?: number + defaultErrorHighlight?: boolean }) => { const [resultVisible, setResultVisible] = useState(defaultResultVisible) const { radii, colors, ns, maxWidths, spaces, fontSizes } = useTheme() @@ -88,21 +90,16 @@ const CodeBlock = ({ shouldHighlightResult(lineIndex, tokenIndex))) && css` font-weight: bold; - background: ${shouldHighlightResult && resultVisible && resultError + background: ${((shouldHighlightResult && resultVisible) || + defaultErrorHighlight) && + resultError ? colors('white') : colors('yellowHighlight')}; - border-bottom: ${shouldHighlightResult && - resultVisible && + border-bottom: ${((shouldHighlightResult && resultVisible) || + defaultErrorHighlight) && resultError - ? 'none' + ? `3px solid ${colors('red')}` : `2px solid ${colors('darkOrange')}`}; - text-decoration: ${shouldHighlightResult && - resultVisible && - resultError - ? 'underline' - : 'none'}; - text-decoration-style: wavy; - text-decoration-color: ${colors('red')}; ` } language={noHighlight ? 'diff' : 'typescript'} diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts index f6ffd96..b808f9e 100644 --- a/src/lib/snippets.ts +++ b/src/lib/snippets.ts @@ -530,6 +530,18 @@ function toggleTodo( // ... }` +export const lgci = `// Same as before +type Todo = Readonly<{ + id: number + text: string + done: boolean +}> + +// Input is an array of Todo items: Todo[] +function completeAll(todos: Todo[]) { + // ... +}` + export const lieq = `type Todo = { id: number text: string @@ -547,6 +559,20 @@ console.log(\`{ id: 1, text: '…', done: false }\`) console.log('Actual:') console.log(result)` +export const mnmy = `// Output is an array of Todo items: Todo[] +function completeAll(todos: Todo[]): Todo[] { + // ... +}` + +export const mwrj = `// After declaring todos as: readonly Todo[], +// the following code WILL NOT compile: + +// Compile error - modifies the array +todos[0] = { id: 1, text: '…', done: true } + +// Compile error - push() modifies the array +todos.push({ id: 1, text: '…', done: true })` + export const njgr = `function toggleTodo(todo: Todo): Todo { // Little Duckling’s refactoring is a // bad refactoring because it modifies @@ -605,6 +631,13 @@ export const reel = `function toggleTodo(todo) { } }` +export const szan = `// Make input todos as readonly array +function completeAll( + todos: readonly Todo[] +): Todo[] { + // ... +}` + export const tdbp = `// Takes an array of todo items and returns // a new array where "done" is all true function completeAll(todos) { diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx index 119c387..968f4df 100644 --- a/src/pages/todo.tsx +++ b/src/pages/todo.tsx @@ -714,7 +714,7 @@ const Page = () => (

lineNumber <= 1} + shouldHighlight={lineIndex => lineIndex <= 1} /> ) @@ -739,9 +739,9 @@ const Page = () => (

The above code is equivalent to the following version:

- (lineNumber === 1 && tokenNumber >= 7 && tokenNumber <= 8) || - (lineNumber === 5 && tokenNumber >= 1) + shouldHighlight={(lineIndex, tokenIndex) => + (lineIndex === 1 && tokenIndex >= 7 && tokenIndex <= 8) || + (lineIndex === 5 && tokenIndex >= 1) } />

@@ -773,8 +773,8 @@ const Page = () => (

In that case, we can write the following code:

- lineNumber === 11 && tokenNumber >= 1 + shouldHighlight={(lineIndex, tokenIndex) => + lineIndex === 11 && tokenIndex >= 1 } /> (

- (lineNumber === 0 && tokenNumber <= 2) || - (lineNumber === 8 && - ((tokenNumber >= 5 && tokenNumber <= 7) || - tokenNumber === 11)) + shouldHighlight={(lineIndex, tokenIndex) => + (lineIndex === 0 && tokenIndex <= 2) || + (lineIndex === 8 && + ((tokenIndex >= 5 && tokenIndex <= 7) || tokenIndex === 11)) } />

@@ -888,9 +887,9 @@ const Page = () => (

- (lineNumber === 1 && tokenNumber >= 7 && tokenNumber <= 8) || - (lineNumber === 5 && tokenNumber >= 1) + shouldHighlight={(lineIndex, tokenIndex) => + (lineIndex === 1 && tokenIndex >= 7 && tokenIndex <= 8) || + (lineIndex === 5 && tokenIndex >= 1) } />

Next, let’s take a look at more non-trivial examples!

@@ -933,6 +932,126 @@ const Page = () => ( true.

+

+ Before implementing it,{' '} + + let’s add some types for the input and output of this function + {' '} + to prevent mistakes! +

+ + ) + }, + { + title: ( + <> + Adding types for completeAll() + + ), + content: ( + <> +

+ First, we’ll specify the type for the input + parameter of completeAll(). It’s an array of{' '} + Todo items.{' '} + + To specify an array type, we add [] next to the + type + {' '} + as follows: +

+ + lineIndex === 8 && tokenIndex >= 5 && tokenIndex <= 9 + } + /> +

+ Second, let’s specify the return type. It’ll also + be an array of Todo items, so we’ll use the same + syntax as above: +

+ + lineIndex === 1 && tokenIndex >= 11 && tokenIndex <= 15 + } + /> +

+ Third, we want to make sure that{' '} + completeAll() returns a new array and does NOT modify + the original array. Each item in the array is already{' '} + readonly, but the array itself is NOT{' '} + readonly yet. +

+

+ To make the array itself readonly,{' '} + + we’ll add the readonly keyword to{' '} + Todo[] like so: + +

+ + lineIndex === 2 && tokenIndex >= 1 + } + /> + +

+ So for arrays, we use the readonly keyword + instead of the Readonly<...> mapped + type? +

+ + ) + } + ]} + /> +

+ Yes, Little Duckling! We use the readonly keyword for + arrays. And by doing so,{' '} + + TypeScript will prevent you from accidently modifying the array. + +

+ + (lineIndex === 4 && tokenIndex <= 3) || + (lineIndex === 7 && tokenIndex <= 2) + } + /> + +

+ Awesome! So, can we start implementing{' '} + completeAll() now? +

+ + ) + } + ]} + /> +

+ Actually:{' '} + + There’s one more thing we’d like to do before we implement{' '} + completeAll() + + . Let’s take a look at what that is! +

) },