+ The new CompletedTodo is almost identical to{' '}
+ Todo, except it has done: true instead
+ of done: boolean.{' '}
+
+ In TypeScript, you can use exact values (like{' '}
+ true or false) when specifying a type.
+
+
+
+ We can now specify the return type of completeAll(){' '}
+ to be an array of CompletedTodo’s:
+
+ >
+ )
+ }
+ ]}
+ />
+ >
+ )
+ },
underConstructionCard
]}
/>
From 9308118087d32ebadb9db49f6c6910a5f5f19ec8 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Mon, 9 Dec 2019 00:41:18 -0800
Subject: [PATCH 02/57] Intersection types
---
snippets/snippets/todo/qnwc.ts | 11 ++++
snippets/snippets/todo/rmuo.ts | 10 +++
snippets/snippets/todo/wdjp.ts | 11 ++++
snippets/snippets/todo/xrwn.ts | 13 ++++
src/components/Emoji/A.tsx | 16 +++++
src/components/Emoji/Ampersand.tsx | 15 +++++
src/components/Emoji/B.tsx | 16 +++++
src/components/Emoji/index.tsx | 8 ++-
src/lib/snippets.ts | 49 ++++++++++++++
src/pages/todo.tsx | 101 +++++++++++++++++++++++++++--
10 files changed, 245 insertions(+), 5 deletions(-)
create mode 100644 snippets/snippets/todo/qnwc.ts
create mode 100644 snippets/snippets/todo/rmuo.ts
create mode 100644 snippets/snippets/todo/wdjp.ts
create mode 100644 snippets/snippets/todo/xrwn.ts
create mode 100644 src/components/Emoji/A.tsx
create mode 100644 src/components/Emoji/Ampersand.tsx
create mode 100644 src/components/Emoji/B.tsx
diff --git a/snippets/snippets/todo/qnwc.ts b/snippets/snippets/todo/qnwc.ts
new file mode 100644
index 0000000..dd316e0
--- /dev/null
+++ b/snippets/snippets/todo/qnwc.ts
@@ -0,0 +1,11 @@
+// They booth have property x,
+// but B’s x (true) is
+// more specific than A’s x (boolean)
+type A = { x: boolean }
+type B = { x: true }
+
+// This intersection type…
+type AandB = A & B
+
+// …is equivalent to:
+type AandB = { x: true }
diff --git a/snippets/snippets/todo/rmuo.ts b/snippets/snippets/todo/rmuo.ts
new file mode 100644
index 0000000..ac11604
--- /dev/null
+++ b/snippets/snippets/todo/rmuo.ts
@@ -0,0 +1,10 @@
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>
+
+// Override the done property of Todo
+type CompletedTodo = Todo & {
+ readonly done: true
+}
diff --git a/snippets/snippets/todo/wdjp.ts b/snippets/snippets/todo/wdjp.ts
new file mode 100644
index 0000000..26ddee3
--- /dev/null
+++ b/snippets/snippets/todo/wdjp.ts
@@ -0,0 +1,11 @@
+type A = { a: number }
+type B = { b: string }
+
+// This intersection type…
+type AandB = A & B
+
+// …is equivalent to:
+type AandB = {
+ a: number
+ b: string
+}
diff --git a/snippets/snippets/todo/xrwn.ts b/snippets/snippets/todo/xrwn.ts
new file mode 100644
index 0000000..96ef30b
--- /dev/null
+++ b/snippets/snippets/todo/xrwn.ts
@@ -0,0 +1,13 @@
+type Todo = Readonly<{
+ // id and text are the same as CompletedTodo
+ id: number
+ text: string
+ done: boolean
+}>
+
+type CompletedTodo = Readonly<{
+ // id and text are the same as Todo
+ id: number
+ text: string
+ done: true
+}>
diff --git a/src/components/Emoji/A.tsx b/src/components/Emoji/A.tsx
new file mode 100644
index 0000000..e666576
--- /dev/null
+++ b/src/components/Emoji/A.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+
+const SvgA = (props: React.SVGProps) => (
+
+)
+
+export default SvgA
diff --git a/src/components/Emoji/Ampersand.tsx b/src/components/Emoji/Ampersand.tsx
new file mode 100644
index 0000000..18829ea
--- /dev/null
+++ b/src/components/Emoji/Ampersand.tsx
@@ -0,0 +1,15 @@
+import React from 'react'
+
+const SvgAmpersand = (props: React.SVGProps) => (
+
+)
+
+export default SvgAmpersand
diff --git a/src/components/Emoji/B.tsx b/src/components/Emoji/B.tsx
new file mode 100644
index 0000000..704481d
--- /dev/null
+++ b/src/components/Emoji/B.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+
+const SvgB = (props: React.SVGProps) => (
+
+)
+
+export default SvgB
diff --git a/src/components/Emoji/index.tsx b/src/components/Emoji/index.tsx
index 48b3c66..44b29e2 100644
--- a/src/components/Emoji/index.tsx
+++ b/src/components/Emoji/index.tsx
@@ -17,6 +17,9 @@ import UpdatedUI from 'src/components/Emoji/UpdatedUi'
import Sweat from 'src/components/Emoji/Sweat'
import TransformTypechecked from 'src/components/Emoji/TransformTypechecked'
import Readonly from 'src/components/Emoji/Readonly'
+import A from 'src/components/Emoji/A'
+import B from 'src/components/Emoji/B'
+import Ampersand from 'src/components/Emoji/Ampersand'
export const emojiToComponent = {
bird: Bird,
@@ -35,7 +38,10 @@ export const emojiToComponent = {
updatedData: UpdatedData,
sweat: Sweat,
transformTypechecked: TransformTypechecked,
- readonly: Readonly
+ readonly: Readonly,
+ a: A,
+ b: B,
+ ampersand: Ampersand
}
export const EmojiWrapper = ({
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index f13c033..0df0341 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -636,6 +636,18 @@ function toggleTodo(todo) {
// ...
}`
+export const qnwc = `// They booth have property x,
+// but B’s x (true) is
+// more specific than A’s x (boolean)
+type A = { x: boolean }
+type B = { x: true }
+
+// This intersection type…
+type AandB = A & B
+
+// …is equivalent to:
+type AandB = { x: true }`
+
export const reel = `function toggleTodo(todo) {
return {
text: todo.text,
@@ -655,6 +667,17 @@ type CompletedTodo = Readonly<{
done: true
}>`
+export const rmuo = `type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>
+
+// Override the done property of Todo
+type CompletedTodo = Todo & {
+ readonly done: true
+}`
+
export const szan = `// Make input todos as readonly array
function completeAll(
todos: readonly Todo[]
@@ -701,6 +724,18 @@ function toggleTodo(todo) {
// { id: …, text: '…', done: true }
}`
+export const wdjp = `type A = { a: number }
+type B = { b: string }
+
+// This intersection type…
+type AandB = A & B
+
+// …is equivalent to:
+type AandB = {
+ a: number
+ b: string
+}`
+
export const wymp = `const originalTodo = {
id: 1,
text: '…',
@@ -718,6 +753,20 @@ console.log(originalTodo)
console.log('New Todo:')
console.log(newTodo)`
+export const xrwn = `type Todo = Readonly<{
+ // id and text are the same as CompletedTodo
+ id: number
+ text: string
+ done: boolean
+}>
+
+type CompletedTodo = Readonly<{
+ // id and text are the same as Todo
+ id: number
+ text: string
+ done: true
+}>`
+
export const yhto = `type Todo = {
readonly id: number
readonly text: string
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index aa60a16..f132d7c 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1108,9 +1108,12 @@ const Page = () => (
}
/>
- By doing this, TypeScript will force you to return an array of
- todo items where done is true—if not, it
- will result in a compile error.
+ By doing this,{' '}
+
+ TypeScript will force you to return an array of todo items where{' '}
+ done is true
+
+ —if not, it will result in a compile error.
+ By doing the above, you can define CompleteTodo to
+ have the same properties as Todo except for{' '}
+ done—without duplicating code.
+
+
+ Summary:{' '}
+
+ Just like JavaScript has boolean operators like{' '}
+ &&, TypeScript has{' '}
+ type operators like & which
+ lets you combine two types.
+
+
>
)
},
+ {
+ title: (
+ <>
+ Section 1 of 4
+ >
+ ),
+ heading: <>Types, Read-only Properties, and Mapped Types>,
+ color: 'darkGreen'
+ },
{
title: <>Transform data into UI>,
content: (
@@ -819,6 +842,7 @@ const Page = () => (
},
{
color: 'green',
+ subtitle: <>Section 1 Summary:>,
title: <>Types are like lightweight, automatic unit tests>,
content: (
<>
@@ -895,6 +919,15 @@ const Page = () => (
>
)
},
+ {
+ title: (
+ <>
+ Section 2 of 4
+ >
+ ),
+ heading: <>Array Types, Literal Types, and Intersection Types>,
+ color: 'darkGreen'
+ },
{
title: <>Mark all as completed>,
content: (
@@ -1092,10 +1125,20 @@ const Page = () => (
Todo, except it has done: true instead
of done: boolean.{' '}
- In TypeScript, you can use exact values (like{' '}
+ In TypeScript, you can use exact values (like{' '}
true or false) when specifying a type.
-
+ {' '}
+ This is called literal types.
+
+ You can use exact values when specifying a type. This is
+ called literal types.
+ >
+ }
+ />
We can now specify the return type of completeAll(){' '}
to be an array of CompletedTodo’s:
From 440a60c195ca383511ed889ca04a595956c345e6 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 10 Dec 2019 17:26:26 -0800
Subject: [PATCH 05/57] Add example to slide 14
---
snippets/snippets/todo/ruga.ts | 5 ++
snippets/snippets/todo/zswn.ts | 6 ++
src/lib/snippets.ts | 13 +++++
src/pages/todo.tsx | 104 ++++++++++++++++++++++++++++-----
4 files changed, 112 insertions(+), 16 deletions(-)
create mode 100644 snippets/snippets/todo/ruga.ts
create mode 100644 snippets/snippets/todo/zswn.ts
diff --git a/snippets/snippets/todo/ruga.ts b/snippets/snippets/todo/ruga.ts
new file mode 100644
index 0000000..3ccde1e
--- /dev/null
+++ b/snippets/snippets/todo/ruga.ts
@@ -0,0 +1,5 @@
+function completeAll(
+ todos: readonly Todo[]
+): Todo[] {
+ // ...
+}
diff --git a/snippets/snippets/todo/zswn.ts b/snippets/snippets/todo/zswn.ts
new file mode 100644
index 0000000..2b458a8
--- /dev/null
+++ b/snippets/snippets/todo/zswn.ts
@@ -0,0 +1,6 @@
+// Will this compile?
+const testTodo: CompletedTodo = {
+ id: 1,
+ text: '…',
+ done: false
+}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 1989109..dceb2cb 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -694,6 +694,12 @@ type CompletedTodo = Todo & {
readonly done: true
}`
+export const ruga = `function completeAll(
+ todos: readonly Todo[]
+): Todo[] {
+ // ...
+}`
+
export const szan = `// Make input todos as readonly array
function completeAll(
todos: readonly Todo[]
@@ -812,3 +818,10 @@ export const yxjg = `function toggleTodo(todo) {
done: !todo.done
}
}`
+
+export const zswn = `// Will this compile?
+const testTodo: CompletedTodo = {
+ id: 1,
+ text: '…',
+ done: false
+}`
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index ec8ce6d..f92432d 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -107,9 +107,9 @@ const Page = () => (
This tutorial doesn’t rely on any specific frontend library
, so it doesn’t matter whether you know React, Vue, or some
- other libraries. As long as you know how to build a todo list
- using a JS library, you should be able to understand this
- tutorial. No prior TypeScript knowledge is necessary.
+ other libraries. As long as you have basic JS knowledge, you
+ should be able to understand this tutorial. No prior TypeScript
+ knowledge is necessary.
To save time,{' '}
@@ -127,23 +127,47 @@ const Page = () => (
I’m not going to cover everything about TypeScript.
{' '}
- I’m only going to cover some of the cool concepts in
- TypeScript—mostly basic, some intermediate-level concepts. My
- goal is to make you want to learn more.
+ I’m only going to cover some of the coolest concepts in
+ TypeScript—mostly basic. My goal is to make you want to learn
+ more.
+
+ I’m only going to cover TypeScript basics. My goal is to make
+ you want to learn more.
+ >
+ }
+ />
- There are 4 sections total in this article. Here
- are the topics covered in each section:
+ There are 3 sections total in this article, plus
+ “one more thing” at the end. Here are the topics covered in each
+ section—there are a total of 10 topics covered:
- Section 1: Types, Read-only Properties, and
- Mapped Types
+ Section 1:{' '}
+
+ Types, Read-only Properties, and Mapped Types
+
- Section 2: Array Types, Literal Types, and
- Intersection Types
+ Section 2:{' '}
+
+ Array Types, Literal Types, and Intersection Types
+
+
+
+ Section 3:{' '}
+
+ Union Types, Discriminated Unions, and Optional Properties
+
+
+
+ One more thing:{' '}
+ Indexed Access Operator
- We can now specify the return type of completeAll(){' '}
- to be an array of CompletedTodo’s:
+ Let’s take a look at an example. In the following code, we added a{' '}
+ CompletedTodo to a todo item that has{' '}
+ done: false. Let’s see what happens{' '}
+
+ when you it
+
+ :
+
+ Type 'false' is not assignable to type 'true'.>}
+ shouldHighlightResult={lineNumber => lineNumber === 4}
+ />
+
+ It failed to compile because done is not{' '}
+ true. By using literal types like the above, you can
+ specify exactly what value is allowed for each property.
+
+
+ Coming back to completeAll(), we can now specify the
+ return type of completeAll() to be an array of{' '}
+ CompletedTodo’s:
+ >
+ )
+ },
underConstructionCard
]}
/>
From 8897eaa8d7ea578451d4e49569ad92ed867c8679 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 10 Dec 2019 17:36:30 -0800
Subject: [PATCH 06/57] Done with section 2
---
snippets/snippets/todo/bpmz.ts | 5 +++++
src/lib/articles.ts | 2 +-
src/lib/snippets.ts | 6 ++++++
src/pages/todo.tsx | 35 +++++++++++++++++++++++++++++++---
4 files changed, 44 insertions(+), 4 deletions(-)
create mode 100644 snippets/snippets/todo/bpmz.ts
diff --git a/snippets/snippets/todo/bpmz.ts b/snippets/snippets/todo/bpmz.ts
new file mode 100644
index 0000000..38a0d93
--- /dev/null
+++ b/snippets/snippets/todo/bpmz.ts
@@ -0,0 +1,5 @@
+type CompletedTodo = Readonly<{
+ id: number
+ text: string
+ done: true
+}>
diff --git a/src/lib/articles.ts b/src/lib/articles.ts
index 78b0502..a65db70 100644
--- a/src/lib/articles.ts
+++ b/src/lib/articles.ts
@@ -8,7 +8,7 @@ export const articlesData = {
todo: {
title:
'TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App',
- date: DateTime.fromISO('2019-12-01T12:00:00Z'),
+ date: DateTime.fromISO('2019-12-13T12:00:00Z'),
description: 'Learn TypeScript by Building a Todo App',
ogImage: 'todo'
},
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index dceb2cb..96533f8 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -484,6 +484,12 @@ export const bnli = `const foo: Todo = {
done: true
}`
+export const bpmz = `type CompletedTodo = Readonly<{
+ id: number
+ text: string
+ done: true
+}>`
+
export const csum = `// todo must match the Todo type
function toggleTodo(todo: Todo) {
// ...
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index f92432d..4dd1ece 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1182,7 +1182,7 @@ const Page = () => (
It failed to compile because done is not{' '}
true. By using literal types like the above, you can
- specify exactly what value is allowed for each property.
+ specify exactly what value is allowed for a property.
Coming back to completeAll(), we can now specify the
@@ -1389,7 +1389,10 @@ const Page = () => (
title: <>TypeScript can handle arrays and exact values>,
content: (
<>
-
In this section, we’ve learned the following:
+
+ In this section, we’ve learned that TypeScript can handle arrays
+ and exact values:
+
1. We can specify an array type by adding []. We
@@ -1404,7 +1407,33 @@ const Page = () => (
}
/>
- 2.
+
+ 2. We can use literal types to specify exactly which value is
+ allowed for a property.
+
+
+ lineNumber === 3}
+ />
+
+ Finally, we learned that{' '}
+
+ we can use intersection types to override some properties and
+ remove code duplication.
+
+
Some todo apps allow you to{' '}
- mark all items as completed. On the
- following todo app,{' '}
+ mark all items as completed. On the following
+ todo app,{' '}
try pressing “Mark all as completed”:
+
+ Sometimes, you might want to separate a group of todo items from
+ others.
+ {' '}
+ On the following todo app, the third item is a{' '}
+ separator which visually separates the first two
+ todo items from the last.
+
+ There’s a separator after the second todo item>}
+ defaultData={[
+ { id: 1, text: 'First todo', done: false },
+ { id: 2, text: 'Second todo', done: false },
+ { type: 'separator' },
+ { id: 3, text: 'Third todo', done: false }
+ ]}
+ />
+ >
+ )
+ },
underConstructionCard
]}
/>
From 254bd741a2ef74933515d3f6e2a4de3da9dfe8f5 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Thu, 12 Dec 2019 05:04:20 +0900
Subject: [PATCH 08/57] Finish slide 20
---
snippets/snippets/todo/jnuw.ts | 11 ++++
snippets/snippets/todo/wmgl.ts | 9 +++
snippets/snippets/todo/yvum.ts | 11 ++++
src/components/Emoji/Type.tsx | 16 ++++++
src/components/Emoji/index.tsx | 4 +-
src/components/TodoList.tsx | 4 +-
src/components/TodoWithData.tsx | 9 +--
src/lib/snippets.ts | 34 +++++++++++
src/pages/todo.tsx | 99 +++++++++++++++++++++++++++++++--
9 files changed, 186 insertions(+), 11 deletions(-)
create mode 100644 snippets/snippets/todo/jnuw.ts
create mode 100644 snippets/snippets/todo/wmgl.ts
create mode 100644 snippets/snippets/todo/yvum.ts
create mode 100644 src/components/Emoji/Type.tsx
diff --git a/snippets/snippets/todo/jnuw.ts b/snippets/snippets/todo/jnuw.ts
new file mode 100644
index 0000000..9444e8d
--- /dev/null
+++ b/snippets/snippets/todo/jnuw.ts
@@ -0,0 +1,11 @@
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+ kind: 'todo'
+}>
+
+type Separator = Readonly<{
+ id: number
+ kind: 'separator'
+}>
diff --git a/snippets/snippets/todo/wmgl.ts b/snippets/snippets/todo/wmgl.ts
new file mode 100644
index 0000000..a01be87
--- /dev/null
+++ b/snippets/snippets/todo/wmgl.ts
@@ -0,0 +1,9 @@
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>
+
+type Separator = Readonly<{
+ id: number
+}>
diff --git a/snippets/snippets/todo/yvum.ts b/snippets/snippets/todo/yvum.ts
new file mode 100644
index 0000000..b90f533
--- /dev/null
+++ b/snippets/snippets/todo/yvum.ts
@@ -0,0 +1,11 @@
+// In addition to the Todo type…
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>
+
+// We need to create the new Separator type?
+type Separator = Readonly<{
+ // ???
+}>
diff --git a/src/components/Emoji/Type.tsx b/src/components/Emoji/Type.tsx
new file mode 100644
index 0000000..796f3c0
--- /dev/null
+++ b/src/components/Emoji/Type.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+
+const SvgType = (props: React.SVGProps) => (
+
+)
+
+export default SvgType
diff --git a/src/components/Emoji/index.tsx b/src/components/Emoji/index.tsx
index 413a66d..143766a 100644
--- a/src/components/Emoji/index.tsx
+++ b/src/components/Emoji/index.tsx
@@ -21,6 +21,7 @@ import A from 'src/components/Emoji/A'
import B from 'src/components/Emoji/B'
import Ampersand from 'src/components/Emoji/Ampersand'
import DoneTrue from 'src/components/Emoji/DoneTrue'
+import Type from 'src/components/Emoji/Type'
export const emojiToComponent = {
bird: Bird,
@@ -43,7 +44,8 @@ export const emojiToComponent = {
a: A,
b: B,
ampersand: Ampersand,
- doneTrue: DoneTrue
+ doneTrue: DoneTrue,
+ type: Type
}
export const EmojiWrapper = ({
diff --git a/src/components/TodoList.tsx b/src/components/TodoList.tsx
index cbb1120..f589724 100644
--- a/src/components/TodoList.tsx
+++ b/src/components/TodoList.tsx
@@ -19,7 +19,7 @@ const TodoList = ({
return (
<>
{items.map((item, index) =>
- item.type !== 'separator' ? (
+ item.kind !== 'separator' ? (
) : (
{
switch (action.type) {
case 'toggle': {
const item = state.todos[action.index]
- if (item.type !== 'separator') {
+ if (item.kind !== 'separator') {
return {
todos: [
...state.todos.slice(0, action.index),
@@ -71,7 +72,7 @@ const reducer = (state: TodoState, action: TodoAction) => {
case 'markAllAsCompleted':
return {
todos: state.todos.map(item =>
- item.type !== 'separator' ? { ...item, done: true } : item
+ item.kind !== 'separator' ? { ...item, done: true } : item
),
lastChangedIndices: state.todos.map((_, index) => index)
}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 96533f8..efca7d2 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -545,6 +545,18 @@ function toggleTodo(
// ...
}`
+export const jnuw = `type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+ kind: 'todo'
+}>
+
+type Separator = Readonly<{
+ id: number
+ kind: 'separator'
+}>`
+
export const kuzw = `function completeAll(todos: Todo[]): Todo[] {
// We want it to return a new array
// instead of modifying the original array
@@ -774,6 +786,16 @@ export const whae = `function completeAll(
}))
}`
+export const wmgl = `type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>
+
+type Separator = Readonly<{
+ id: number
+}>`
+
export const wymp = `const originalTodo = {
id: 1,
text: '…',
@@ -811,6 +833,18 @@ export const yhto = `type Todo = {
readonly done: boolean
}`
+export const yvum = `// In addition to the Todo type…
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>
+
+// We need to create the new Separator type?
+type Separator = Readonly<{
+ // ???
+}>`
+
export const ywiv = `// The return value must match the Todo type
function toggleTodo(todo: Todo): Todo {
// ...
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 862a81c..9957fbe 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1445,24 +1445,115 @@ const Page = () => (
title: <>Separators>,
content: (
<>
+
+ Let me introduce the new feature of our todo app called{' '}
+ separators.{' '}
+
Sometimes, you might want to separate a group of todo items from
others.
{' '}
- On the following todo app, the third item is a{' '}
- separator which visually separates the first two
- todo items from the last.
+ On the following todo app, the third item is a separator which
+ visually separates the first two todo items from the last.
+ Now, here’s a question:{' '}
+
+ What type should we create to support these
+ separator items?
+
+
+
+ What type should we create to support separator
+ items?
+ >
+ }
+ />
+ >
+ )
+ },
+ {
+ title: (
+ <>
+ The Separator type
+ >
+ ),
+ content: (
+ <>
+
+
+ Let me guess: In addition to the
+ existing Todo type, we need to create the
+ new Separator type to describe a separator
+ item, right? Something like this?
+
+ Exactly, Little Duckling! Now, let’s think about
+ what needs to go into the Separator type.{' '}
+
+
+ First,{' '}
+
+ we’ll add id just like todos
+ {' '}
+ because we’ll probably need to store the separator data into a
+ backend database using this id.
+
+ lineIndex === 7}
+ />
+
+ Second,{' '}
+
+ let’s add a kind property to BOTH Todo{' '}
+ and Separator to differentiate them.
+
+
+ For each type, we’ll specify exactly what value is allowed for the{' '}
+ kind property (reminder—this is called{' '}
+ literal types). The Todo item must
+ have kind: 'todo', and the Separator{' '}
+ item must have kind: 'separator'.
- By doing the above, you can define CompleteTodo to
+ By doing the above, you can define CompletedTodo to
have the same properties as Todo except for{' '}
done—without duplicating code.
+ Now, let’s revisit the mark all as completed{' '}
+ feature again. If you{' '}
+ press “Mark all as completed” below, it
+ ignores the separators and only toggles the todo items.
+
+ However, now the array can contain both{' '}
+ Todos and Separators.
+
+
+
+ To represent this, we need to use a TypeScript feature called{' '}
+ union types. In TypeScript, you can use the
+ syntax A | B to create a union type, which represents
+ a type that’s{' '}
+
+ either A or B
+
+ .
+
+
+ A | B is an union type, which
+ means{' '}
+
+ either A or B.
+
+ >
+ }
+ />
+
+
+ In this case, we need to create a union of Todo and{' '}
+ Separator like this:
+
+
+
+ Because the input to completeAll() is an array of{' '}
+ Todo | Separator’s, we can update the parameter
+ type like below.
+ {' '}
+ (Also: I’ve updated the parameter name from todos to{' '}
+ items.)
+
+ lineNumber === 3}
+ />
+
+ How about the ouptut (return) type? Before, we
+ set it as an array of CompletedTodo’s:
+
+ We can use the union type here again.{' '}
+
+ completeAll() should return an array where each
+ item is either CompletedTodo or{' '}
+ Separator
+
+ , so we’ll specify the output type like this:
+
To get this to work, we need to modify our{' '}
completeAll() function to support the{' '}
@@ -1709,7 +1713,11 @@ const Page = () => (
)
},
{
- title: <>Discriminated unions>,
+ title: (
+ <>
+ Implementing completeAll()
+ >
+ ),
content: (
<>
@@ -1723,7 +1731,48 @@ const Page = () => (
Let’s now think about what the updated version (with updated
types) would look like:
-
+ lineNumber === 3}
+ />
+
+ First, the .map() part would be the same as before—we
+ just need to change what goes inside .map().
+
+ Now, let’s take a look at the todo app again. If you press{' '}
+ “Mark all as completed”, here’s what
+ happens to each item:
+
+
+
+ If it’s{' '}
+
+ a Todo, change done to{' '}
+ true.
+
+
+
+ If it’s{' '}
+
+ a Separator, do nothing.
+
+
+
+
+
+
+ We can check the kind property to differentiate
+ between a Todo and a Separator
+
+ . So let’s do that inside .map():
+
>
)
},
From bacbf5ee90e31b7b405fbade6859468da93a3fec Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Sat, 14 Dec 2019 12:00:44 +0900
Subject: [PATCH 15/57] Optional properties
---
snippets/snippets/todo/bxzu.ts | 12 ++++++
snippets/snippets/todo/kquk.ts | 6 +++
src/lib/snippets.ts | 20 ++++++++++
src/pages/todo.tsx | 70 +++++++++++++++++++++++++++++++---
4 files changed, 103 insertions(+), 5 deletions(-)
create mode 100644 snippets/snippets/todo/bxzu.ts
create mode 100644 snippets/snippets/todo/kquk.ts
diff --git a/snippets/snippets/todo/bxzu.ts b/snippets/snippets/todo/bxzu.ts
new file mode 100644
index 0000000..b97fef3
--- /dev/null
+++ b/snippets/snippets/todo/bxzu.ts
@@ -0,0 +1,12 @@
+type Todo = {
+ // Make id optional
+ id?: number
+ text: string
+ done: boolean
+}
+
+// This will now compile!
+const bar: Todo = {
+ text: '…',
+ done: true
+}
diff --git a/snippets/snippets/todo/kquk.ts b/snippets/snippets/todo/kquk.ts
new file mode 100644
index 0000000..840dc8d
--- /dev/null
+++ b/snippets/snippets/todo/kquk.ts
@@ -0,0 +1,6 @@
+type Todo = {
+ // Make id optional
+ id?: number
+ text: string
+ done: boolean
+}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 5f9b060..9b7a25d 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -504,6 +504,19 @@ export const bpmz = `type CompletedTodo = Readonly<{
done: true
}>`
+export const bxzu = `type Todo = {
+ // Make id optional
+ id?: number
+ text: string
+ done: boolean
+}
+
+// This will now compile!
+const bar: Todo = {
+ text: '…',
+ done: true
+}`
+
export const csum = `// todo must match the Todo type
function toggleTodo(todo: Todo) {
// ...
@@ -575,6 +588,13 @@ type Separator = Readonly<{
kind: 'separator'
}>`
+export const kquk = `type Todo = {
+ // Make id optional
+ id?: number
+ text: string
+ done: boolean
+}`
+
export const kuzw = `function completeAll(todos: Todo[]): Todo[] {
// We want it to return a new array
// instead of modifying the original array
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 3176aef..87108cb 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -20,9 +20,10 @@ import BubbleQuotes from 'src/components/BubbleQuotes'
import ResultHighlight from 'src/components/ResultHighlight'
const compileSuccess = 'Compiled successfully!'
-const section1 = 'Types, Read-only Properties, and Mapped Types'
+const section1 =
+ 'Types, Optional Properties, Read-only Properties, and Mapped Types'
const section2 = 'Array Types, Literal Types, and Intersection Types'
-const section3 = 'Union Types, Discriminated Unions, and Optional Properties'
+const section3 = 'Union Types and Discriminated Unions'
const TodoWithSeparatorAndMarkAllAsCompleted = () => (
(
There are 3 sections total in this article, plus
“one more thing” at the end. Here are the topics covered in each
- section—there are a total of 10 topics covered:
+ section—there are a total of 9 topics covered:
+
+ What if we actually want to make the{' '}
+ id property of a todo item
+ optional?
+ {' '}
+ For example, a new todo item that’s not yet saved in a database
+ may not have an id yet.
+
+
+ To make a property optional in TypeScript,{' '}
+
+ you can add a question mark (
+ ?)
+ {' '}
+ after the property name:
+
+
+ lineIndex === 2 && tokenIndex <= 1
+ }
+ />
+
+ But for the purposes of our discussion,{' '}
+
+ we’ll keep id as required
+ {' '}
+ to make things simpler.
+
Now, let’s use TypeScript to prevent the mistake Little Duckling
made earlier. To recap, here’s the Todo type we
- created earlier:
+ created earlier (
+
+ id is required
+
+ ):
@@ -901,7 +949,19 @@ const Page = () => (
/>
- 2. We can use the readonly keyword to make sure
+ 2. We can make some properties optional by adding ?{' '}
+ after the property name.
+
+
+
+ lineIndex === 2 && tokenIndex <= 1
+ }
+ />
+
+
+ 3. We can use the readonly keyword to make sure
that an object’s properties are not modified.
From 0e063697b825e47a56ee4d2ef0dd8c650960fa4b Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Sat, 14 Dec 2019 12:27:03 +0900
Subject: [PATCH 16/57] Remove completeAll() rewrite
---
snippets/snippets/todo/afeb.ts | 7 --
snippets/snippets/todo/bepv.ts | 5 -
snippets/snippets/todo/czgn.ts | 3 -
snippets/snippets/todo/sgdc.ts | 9 --
snippets/snippets/todo/vgnb.ts | 4 -
snippets/snippets/todo/wrcf.ts | 9 --
snippets/snippets/todo/ybsb.ts | 8 --
snippets/snippets/todo/ykpe.ts | 5 -
snippets/snippets/todo/zbii.ts | 5 -
src/lib/snippets.ts | 64 -------------
src/pages/todo.tsx | 170 +--------------------------------
11 files changed, 5 insertions(+), 284 deletions(-)
delete mode 100644 snippets/snippets/todo/afeb.ts
delete mode 100644 snippets/snippets/todo/bepv.ts
delete mode 100644 snippets/snippets/todo/czgn.ts
delete mode 100644 snippets/snippets/todo/sgdc.ts
delete mode 100644 snippets/snippets/todo/vgnb.ts
delete mode 100644 snippets/snippets/todo/wrcf.ts
delete mode 100644 snippets/snippets/todo/ybsb.ts
delete mode 100644 snippets/snippets/todo/ykpe.ts
delete mode 100644 snippets/snippets/todo/zbii.ts
diff --git a/snippets/snippets/todo/afeb.ts b/snippets/snippets/todo/afeb.ts
deleted file mode 100644
index 2a76f70..0000000
--- a/snippets/snippets/todo/afeb.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-type CompletedTodo = Todo & {
- readonly done: true
-}
-
-function completeAll(
- items: readonly (Todo | Separator)[]
-): CompletedTodo[] // <- Before
diff --git a/snippets/snippets/todo/bepv.ts b/snippets/snippets/todo/bepv.ts
deleted file mode 100644
index b8e6746..0000000
--- a/snippets/snippets/todo/bepv.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-function completeAll(
- // After: An array of items, each of which
- // can be either Todo or Separator
- items: readonly (Todo | Separator)[]
-)
diff --git a/snippets/snippets/todo/czgn.ts b/snippets/snippets/todo/czgn.ts
deleted file mode 100644
index 5513e1d..0000000
--- a/snippets/snippets/todo/czgn.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] // <- After
diff --git a/snippets/snippets/todo/sgdc.ts b/snippets/snippets/todo/sgdc.ts
deleted file mode 100644
index 861ec6f..0000000
--- a/snippets/snippets/todo/sgdc.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// Previous version of completeAll()
-function completeAll(
- todos: readonly Todo[]
-): CompletedTodo[] {
- return todos.map(todo => ({
- ...todo,
- done: true
- }))
-}
diff --git a/snippets/snippets/todo/vgnb.ts b/snippets/snippets/todo/vgnb.ts
deleted file mode 100644
index bd73fa7..0000000
--- a/snippets/snippets/todo/vgnb.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-function completeAll(
- // Before: An array of Todo’s
- todos: readonly Todo[]
-)
diff --git a/snippets/snippets/todo/wrcf.ts b/snippets/snippets/todo/wrcf.ts
deleted file mode 100644
index 1660625..0000000
--- a/snippets/snippets/todo/wrcf.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// TODO: Need to update to support separators
-function completeAll(
- todos: readonly Todo[]
-): CompletedTodo[] {
- return todos.map(todo => ({
- ...todo,
- done: true
- }))
-}
diff --git a/snippets/snippets/todo/ybsb.ts b/snippets/snippets/todo/ybsb.ts
deleted file mode 100644
index decd23c..0000000
--- a/snippets/snippets/todo/ybsb.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-;[
- // Todo
- { id: 1, kind: 'todo', text: 'A', done: false },
- // Separator
- { id: 1, kind: 'separator' },
- // Todo
- { id: 2, kind: 'todo', text: 'B', done: false }
-]
diff --git a/snippets/snippets/todo/ykpe.ts b/snippets/snippets/todo/ykpe.ts
deleted file mode 100644
index 7b83026..0000000
--- a/snippets/snippets/todo/ykpe.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- return items.map(item => /* ? */)
-}
diff --git a/snippets/snippets/todo/zbii.ts b/snippets/snippets/todo/zbii.ts
deleted file mode 100644
index d7b89d9..0000000
--- a/snippets/snippets/todo/zbii.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- // What would the updated version look like?
-}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 9b7a25d..e8267ef 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -469,14 +469,6 @@ const { getState, setState } = makeState()
setState('foo')
console.log(getState())`
-export const afeb = `type CompletedTodo = Todo & {
- readonly done: true
-}
-
-function completeAll(
- items: readonly (Todo | Separator)[]
-): CompletedTodo[] // <- Before`
-
export const ampt = `function toggleTodo(todo: Todo): Todo {
return {
// This line was missing
@@ -486,12 +478,6 @@ export const ampt = `function toggleTodo(todo: Todo): Todo {
}
}`
-export const bepv = `function completeAll(
- // After: An array of items, each of which
- // can be either Todo or Separator
- items: readonly (Todo | Separator)[]
-)`
-
export const bnli = `const foo: Todo = {
id: 1,
text: '…',
@@ -522,10 +508,6 @@ function toggleTodo(todo: Todo) {
// ...
}`
-export const czgn = `function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] // <- After`
-
export const dqwb = `function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring
todo.done = !todo.done
@@ -762,16 +744,6 @@ export const rymr = `function completeAll(
return items.map(item => /* ? */)
}`
-export const sgdc = `// Previous version of completeAll()
-function completeAll(
- todos: readonly Todo[]
-): CompletedTodo[] {
- return todos.map(todo => ({
- ...todo,
- done: true
- }))
-}`
-
export const szan = `// Make input todos as readonly array
function completeAll(
todos: readonly Todo[]
@@ -796,11 +768,6 @@ export const uxlb = `function toggleTodo(todo: Todo): Todo {
return todo
}`
-export const vgnb = `function completeAll(
- // Before: An array of Todo’s
- todos: readonly Todo[]
-)`
-
export const vgnq = `// This will continue to work because
// the input todo is not modified
function toggleTodo(todo: Todo): Todo {
@@ -883,16 +850,6 @@ function completeAll(
})
}`
-export const wrcf = `// TODO: Need to update to support separators
-function completeAll(
- todos: readonly Todo[]
-): CompletedTodo[] {
- return todos.map(todo => ({
- ...todo,
- done: true
- }))
-}`
-
export const wymp = `const originalTodo = {
id: 1,
text: '…',
@@ -928,27 +885,12 @@ export const xtkd = `// A union type of Todo and Separator.
// This means: "either Todo OR Separator"
Todo | Separator`
-export const ybsb = `[
- // Todo
- { id: 1, kind: 'todo', text: 'A', done: false },
- // Separator
- { id: 1, kind: 'separator' },
- // Todo
- { id: 2, kind: 'todo', text: 'B', done: false }
-]`
-
export const yhto = `type Todo = {
readonly id: number
readonly text: string
readonly done: boolean
}`
-export const ykpe = `function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- return items.map(item => /* ? */)
-}`
-
export const yvum = `// In addition to the Todo type…
type Todo = Readonly<{
id: number
@@ -975,12 +917,6 @@ export const yxjg = `function toggleTodo(todo) {
}
}`
-export const zbii = `function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- // What would the updated version look like?
-}`
-
export const zswn = `// Will this compile?
const testTodo: CompletedTodo = {
id: 1,
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 87108cb..1935c08 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -25,26 +25,6 @@ const section1 =
const section2 = 'Array Types, Literal Types, and Intersection Types'
const section3 = 'Union Types and Discriminated Unions'
-const TodoWithSeparatorAndMarkAllAsCompleted = () => (
-
- ↓ Try pressing “Mark all as completed”
- >
- }
- showMarkAllAsCompleted
- narrowText
- defaultData={[
- { id: 1, kind: 'todo', text: 'A', done: false },
- { id: 1, kind: 'separator' },
- { id: 2, kind: 'todo', text: 'B', done: false }
- ]}
- highlightLineIndexOffset={1}
- shouldHighlight={tokenIndex => tokenIndex === 20}
- />
-)
-
const Page = () => (
(
color: 'darkGreen'
},
{
- title: <>Separators>,
+ title: <>Nea Feature: Separators>,
content: (
<>
@@ -1633,8 +1613,8 @@ const Page = () => (
For each type, we’ll specify exactly what value is allowed for the{' '}
kind property (reminder—this is called{' '}
- literal types). The Todo item must
- have kind: 'todo', and the Separator{' '}
+ literal types). Each Todo item must
+ have kind: 'todo', and each Separator{' '}
item must have kind: 'separator'.
- Now, let’s revisit the mark all as completed{' '}
- feature again. If you{' '}
- press “Mark all as completed” below, it
- ignores the separators and only toggles the todo items.
-
-
-
- To get this to work, we need to modify our{' '}
- completeAll() function to support the{' '}
- Separator type.
-
- lineNumber === 0}
- />
-
-
- First, we need to change the input (parameter) type.
- {' '}
- Previously, the input was just an array of Todos.
-
- However, now the array can contain both{' '}
- Todos and Separators.
-
-
To represent this, we need to use a TypeScript feature called{' '}
union types. In TypeScript, you can use the
@@ -1721,118 +1668,11 @@ const Page = () => (
>
}
/>
-
In this case, we need to create a union of Todo and{' '}
Separator like this:
-
-
- Because the input to completeAll() is an array of{' '}
- Todo | Separator’s, we can update the parameter
- type like below.
- {' '}
- (Also: I’ve updated the parameter name from todos to{' '}
- items.)
-
- lineNumber === 3}
- />
-
- How about the ouptut (return) type? Before, we
- set it as an array of CompletedTodo’s:
-
- We can use the union type here again.{' '}
-
- completeAll() should return an array where each
- item is either CompletedTodo or{' '}
- Separator
-
- , so we’ll specify the output type like this:
-
- There are 3 sections total in this article, plus
- “one more thing” at the end. Here are the topics covered in each
- section—there are a total of 9 topics covered:
+ There are 3 sections total in this article. Here
+ are the topics covered in each section.
From f31124a9c59ce48edc45e1ef5a809a93380cc362 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Sat, 14 Dec 2019 16:07:32 +0900
Subject: [PATCH 18/57] Remove optional properties from section 1
---
snippets/snippets/todo/bxzu.ts | 12 -------
snippets/snippets/todo/kquk.ts | 6 ----
src/lib/snippets.ts | 20 -----------
src/pages/todo.tsx | 62 ++--------------------------------
4 files changed, 3 insertions(+), 97 deletions(-)
delete mode 100644 snippets/snippets/todo/bxzu.ts
delete mode 100644 snippets/snippets/todo/kquk.ts
diff --git a/snippets/snippets/todo/bxzu.ts b/snippets/snippets/todo/bxzu.ts
deleted file mode 100644
index b97fef3..0000000
--- a/snippets/snippets/todo/bxzu.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-type Todo = {
- // Make id optional
- id?: number
- text: string
- done: boolean
-}
-
-// This will now compile!
-const bar: Todo = {
- text: '…',
- done: true
-}
diff --git a/snippets/snippets/todo/kquk.ts b/snippets/snippets/todo/kquk.ts
deleted file mode 100644
index 840dc8d..0000000
--- a/snippets/snippets/todo/kquk.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-type Todo = {
- // Make id optional
- id?: number
- text: string
- done: boolean
-}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index e8267ef..b3a38f1 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -490,19 +490,6 @@ export const bpmz = `type CompletedTodo = Readonly<{
done: true
}>`
-export const bxzu = `type Todo = {
- // Make id optional
- id?: number
- text: string
- done: boolean
-}
-
-// This will now compile!
-const bar: Todo = {
- text: '…',
- done: true
-}`
-
export const csum = `// todo must match the Todo type
function toggleTodo(todo: Todo) {
// ...
@@ -570,13 +557,6 @@ type Separator = Readonly<{
kind: 'separator'
}>`
-export const kquk = `type Todo = {
- // Make id optional
- id?: number
- text: string
- done: boolean
-}`
-
export const kuzw = `function completeAll(todos: Todo[]): Todo[] {
// We want it to return a new array
// instead of modifying the original array
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 0dea701..e278cfb 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -20,10 +20,9 @@ import BubbleQuotes from 'src/components/BubbleQuotes'
import ResultHighlight from 'src/components/ResultHighlight'
const compileSuccess = 'Compiled successfully!'
-const section1 =
- 'Types, Optional Properties, Read-only Properties, and Mapped Types'
+const section1 = 'Types, Read-only Properties, and Mapped Types'
const section2 = 'Array Types, Literal Types, and Intersection Types'
-const section3 = 'Union Types'
+const section3 = 'Union Types and Optional Properties'
const Page = () => (
(
>
)
},
- {
- color: 'pink',
- subtitle: <>Side Note>,
- title: (
- <>
- Optional id?
- >
- ),
- content: (
- <>
-
-
- What if we actually want to make the{' '}
- id property of a todo item
- optional?
- {' '}
- For example, a new todo item that’s not yet saved in a database
- may not have an id yet.
-
-
- To make a property optional in TypeScript,{' '}
-
- you can add a question mark (
- ?)
- {' '}
- after the property name:
-
-
- lineIndex === 2 && tokenIndex <= 1
- }
- />
-
- But for the purposes of our discussion,{' '}
-
- we’ll keep id as required
- {' '}
- to make things simpler.
-
- 2. We can make some properties optional by adding ?{' '}
- after the property name.
-
-
-
- lineIndex === 2 && tokenIndex <= 1
- }
- />
-
-
- 3. We can use the readonly keyword to make sure
+ 2. We can use the readonly keyword to make sure
that an object’s properties are not modified.
From 984e61dd2980213e45e7d1736e5f11445f0154f9 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Sat, 14 Dec 2019 20:13:35 +0900
Subject: [PATCH 19/57] Start location labels
---
snippets/snippets/todo/jnuw.ts | 11 ---
snippets/snippets/todo/rymr.ts | 5 --
snippets/snippets/todo/wmgl.ts | 9 ---
snippets/snippets/todo/wptf.ts | 27 -------
snippets/snippets/todo/xtkd.ts | 3 -
snippets/snippets/todo/yvum.ts | 11 ---
src/components/Emoji/Home.tsx | 28 +++++++
src/components/Emoji/Work.tsx | 24 ++++++
src/components/Emoji/index.tsx | 6 +-
src/components/PlaceLabel.tsx | 32 ++++++++
src/lib/snippets.ts | 72 -----------------
src/pages/todo.tsx | 140 ++-------------------------------
12 files changed, 97 insertions(+), 271 deletions(-)
delete mode 100644 snippets/snippets/todo/jnuw.ts
delete mode 100644 snippets/snippets/todo/rymr.ts
delete mode 100644 snippets/snippets/todo/wmgl.ts
delete mode 100644 snippets/snippets/todo/wptf.ts
delete mode 100644 snippets/snippets/todo/xtkd.ts
delete mode 100644 snippets/snippets/todo/yvum.ts
create mode 100644 src/components/Emoji/Home.tsx
create mode 100644 src/components/Emoji/Work.tsx
create mode 100644 src/components/PlaceLabel.tsx
diff --git a/snippets/snippets/todo/jnuw.ts b/snippets/snippets/todo/jnuw.ts
deleted file mode 100644
index 2a7dbc3..0000000
--- a/snippets/snippets/todo/jnuw.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-type Todo = Readonly<{
- id: number
- kind: 'todo'
- text: string
- done: boolean
-}>
-
-type Separator = Readonly<{
- id: number
- kind: 'separator'
-}>
diff --git a/snippets/snippets/todo/rymr.ts b/snippets/snippets/todo/rymr.ts
deleted file mode 100644
index 7b83026..0000000
--- a/snippets/snippets/todo/rymr.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- return items.map(item => /* ? */)
-}
diff --git a/snippets/snippets/todo/wmgl.ts b/snippets/snippets/todo/wmgl.ts
deleted file mode 100644
index a01be87..0000000
--- a/snippets/snippets/todo/wmgl.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-type Todo = Readonly<{
- id: number
- text: string
- done: boolean
-}>
-
-type Separator = Readonly<{
- id: number
-}>
diff --git a/snippets/snippets/todo/wptf.ts b/snippets/snippets/todo/wptf.ts
deleted file mode 100644
index ed1c60a..0000000
--- a/snippets/snippets/todo/wptf.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-type Todo = Readonly<{
- id: number
- text: string
- done: boolean
- kind: 'todo'
-}>
-
-type Separator = Readonly<{
- id: number
- kind: 'separator'
-}>
-
-type CompletedTodo = Todo & {
- readonly done: true
-}
-
-function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- return items.map(item => {
- if (item.kind === 'todo') {
- return { ...item, done: true }
- } else {
- return item
- }
- })
-}
diff --git a/snippets/snippets/todo/xtkd.ts b/snippets/snippets/todo/xtkd.ts
deleted file mode 100644
index 02a703a..0000000
--- a/snippets/snippets/todo/xtkd.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-// A union type of Todo and Separator.
-// This means: "either Todo OR Separator"
-Todo | Separator
diff --git a/snippets/snippets/todo/yvum.ts b/snippets/snippets/todo/yvum.ts
deleted file mode 100644
index b90f533..0000000
--- a/snippets/snippets/todo/yvum.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// In addition to the Todo type…
-type Todo = Readonly<{
- id: number
- text: string
- done: boolean
-}>
-
-// We need to create the new Separator type?
-type Separator = Readonly<{
- // ???
-}>
diff --git a/src/components/Emoji/Home.tsx b/src/components/Emoji/Home.tsx
new file mode 100644
index 0000000..758fc34
--- /dev/null
+++ b/src/components/Emoji/Home.tsx
@@ -0,0 +1,28 @@
+import React from 'react'
+
+const SvgHome = (props: React.SVGProps) => (
+
+)
+
+export default SvgHome
diff --git a/src/components/Emoji/Work.tsx b/src/components/Emoji/Work.tsx
new file mode 100644
index 0000000..0d76e60
--- /dev/null
+++ b/src/components/Emoji/Work.tsx
@@ -0,0 +1,24 @@
+import React from 'react'
+
+const SvgWork = (props: React.SVGProps) => (
+
+)
+
+export default SvgWork
diff --git a/src/components/Emoji/index.tsx b/src/components/Emoji/index.tsx
index 95c099e..a678bdb 100644
--- a/src/components/Emoji/index.tsx
+++ b/src/components/Emoji/index.tsx
@@ -23,6 +23,8 @@ import Ampersand from 'src/components/Emoji/Ampersand'
import DoneTrue from 'src/components/Emoji/DoneTrue'
import Type from 'src/components/Emoji/Type'
import VerticalBar from 'src/components/Emoji/VerticalBar'
+import Work from 'src/components/Emoji/Work'
+import Home from 'src/components/Emoji/Home'
export const emojiToComponent = {
bird: Bird,
@@ -47,7 +49,9 @@ export const emojiToComponent = {
ampersand: Ampersand,
doneTrue: DoneTrue,
type: Type,
- verticalBar: VerticalBar
+ verticalBar: VerticalBar,
+ work: Work,
+ home: Home
}
export const EmojiWrapper = ({
diff --git a/src/components/PlaceLabel.tsx b/src/components/PlaceLabel.tsx
new file mode 100644
index 0000000..1d688e5
--- /dev/null
+++ b/src/components/PlaceLabel.tsx
@@ -0,0 +1,32 @@
+/** @jsx jsx */
+import { css, jsx } from '@emotion/core'
+import Emoji from 'src/components/Emoji'
+import useTheme from 'src/hooks/useTheme'
+
+const PlaceLabel = ({ place }: { place: 'home' | 'work' }) => {
+ const { colors, spaces, fontSizes, radii } = useTheme()
+ return (
+
+
+
+ {' '}
+ {place === 'home' ? 'Home' : 'Work'}
+
+ )
+}
+
+export default PlaceLabel
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index b3a38f1..96533f8 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -545,18 +545,6 @@ function toggleTodo(
// ...
}`
-export const jnuw = `type Todo = Readonly<{
- id: number
- kind: 'todo'
- text: string
- done: boolean
-}>
-
-type Separator = Readonly<{
- id: number
- kind: 'separator'
-}>`
-
export const kuzw = `function completeAll(todos: Todo[]): Todo[] {
// We want it to return a new array
// instead of modifying the original array
@@ -718,12 +706,6 @@ export const ruga = `function completeAll(
// ...
}`
-export const rymr = `function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- return items.map(item => /* ? */)
-}`
-
export const szan = `// Make input todos as readonly array
function completeAll(
todos: readonly Todo[]
@@ -792,44 +774,6 @@ export const whae = `function completeAll(
}))
}`
-export const wmgl = `type Todo = Readonly<{
- id: number
- text: string
- done: boolean
-}>
-
-type Separator = Readonly<{
- id: number
-}>`
-
-export const wptf = `type Todo = Readonly<{
- id: number
- text: string
- done: boolean
- kind: 'todo'
-}>
-
-type Separator = Readonly<{
- id: number
- kind: 'separator'
-}>
-
-type CompletedTodo = Todo & {
- readonly done: true
-}
-
-function completeAll(
- items: readonly (Todo | Separator)[]
-): (CompletedTodo | Separator)[] {
- return items.map(item => {
- if (item.kind === 'todo') {
- return { ...item, done: true }
- } else {
- return item
- }
- })
-}`
-
export const wymp = `const originalTodo = {
id: 1,
text: '…',
@@ -861,28 +805,12 @@ type CompletedTodo = Readonly<{
done: true
}>`
-export const xtkd = `// A union type of Todo and Separator.
-// This means: "either Todo OR Separator"
-Todo | Separator`
-
export const yhto = `type Todo = {
readonly id: number
readonly text: string
readonly done: boolean
}`
-export const yvum = `// In addition to the Todo type…
-type Todo = Readonly<{
- id: number
- text: string
- done: boolean
-}>
-
-// We need to create the new Separator type?
-type Separator = Readonly<{
- // ???
-}>`
-
export const ywiv = `// The return value must match the Todo type
function toggleTodo(todo: Todo): Todo {
// ...
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index e278cfb..9614bb1 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -18,6 +18,7 @@ import RunButtonText from 'src/components/RunButtonText'
import CodeBlock from 'src/components/CodeBlock'
import BubbleQuotes from 'src/components/BubbleQuotes'
import ResultHighlight from 'src/components/ResultHighlight'
+import PlaceLabel from 'src/components/PlaceLabel'
const compileSuccess = 'Compiled successfully!'
const section1 = 'Types, Read-only Properties, and Mapped Types'
@@ -1441,143 +1442,18 @@ const Page = () => (
color: 'darkGreen'
},
{
- title: <>New feature: Separators>,
+ title: <>New Feature: Location labels>,
content: (
<>
- Let me introduce the new feature of our todo app called{' '}
- separators.{' '}
+ Let’s add a new feature to our todo app:{' '}
+ Location labels.
-
- Sometimes, you might want to separate a group of todo items from
- others.
- {' '}
- On the following todo app, the third item is a separator which
- visually separates the first two todo items from the last.
-
- Now, here’s a question:{' '}
-
- What type should we create to support these
- separator items?
-
-
-
- What type should we create to support separator
- items?
- >
- }
- />
- >
- )
- },
- {
- title: (
- <>
- The Separator type
- >
- ),
- content: (
- <>
-
-
- Let me guess: In addition to the
- existing Todo type, we need to create the
- new Separator type to describe a separator
- item, right? Something like this?
-
- Exactly, Little Duckling! Now, let’s think about
- what needs to go into the Separator type.{' '}
-
-
- First,{' '}
-
- we’ll add id just like todos
- {' '}
- because we’ll probably need to store the separator data into a
- backend database using this id.
+ Each todo item can now optionally be labeled as{' '}
+ or , like
+ this:
- lineIndex === 7}
- />
-
- Second,{' '}
-
- let’s add a kind property to BOTH Todo{' '}
- and Separator to differentiate them.
- {' '}
-
-
- For each type, we’ll specify exactly what value is allowed for the{' '}
- kind property (reminder—this is called{' '}
- literal types). Each Todo item must
- have kind: 'todo', and each Separator{' '}
- item must have kind: 'separator'.
-
- lineIndex === 2 || lineIndex === 9}
- />
-
- That’s it! Let’s now take a look at an example. Here’s a todo list
- containing some separators, displayed with its associated data:
-
To represent this, we need to use a TypeScript feature called{' '}
union types. In TypeScript, you can use the
@@ -1611,7 +1488,6 @@ const Page = () => (
In this case, we need to create a union of Todo and{' '}
Separator like this:
))}
diff --git a/src/components/TodoWithData.tsx b/src/components/TodoWithData.tsx
index 5348a82..380cc73 100644
--- a/src/components/TodoWithData.tsx
+++ b/src/components/TodoWithData.tsx
@@ -26,7 +26,7 @@ export type Todo = {
id: number
text: string
done: boolean
- location?: 'home' | 'work'
+ place?: 'home' | 'work'
}
export type TodoAction =
@@ -79,6 +79,7 @@ const TodoWithData = ({
disabled,
highlightLineIndexOffset,
shouldHighlight,
+ shouldAlwaysHighlight,
narrowText
}: {
defaultData: Todo[]
@@ -90,6 +91,7 @@ const TodoWithData = ({
disabled?: boolean
highlightLineIndexOffset?: number
shouldHighlight?: (tokenIndex: number) => boolean
+ shouldAlwaysHighlight?: (lineIndex: number, tokenIndex: number) => boolean
narrowText?: boolean
}) => {
const { spaces, ns, maxWidths, radii, colors, letterSpacings } = useTheme()
@@ -144,6 +146,15 @@ const TodoWithData = ({
letter-spacing: ${letterSpacings('smallCode')};
`
]}
+ lineCssOverrides={(lineIndex, tokenIndex) =>
+ shouldAlwaysHighlight &&
+ shouldAlwaysHighlight(lineIndex, tokenIndex) &&
+ css`
+ background: ${colors('yellowHighlight')};
+ border-bottom: 2px solid ${colors('darkOrange')};
+ font-weight: bold;
+ `
+ }
lineCssOverridesAnimation={(lineIndex, tokenIndex) =>
shouldHighlight &&
state.lastChangedIndices
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 96533f8..d0466a0 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -706,6 +706,14 @@ export const ruga = `function completeAll(
// ...
}`
+export const rvyq = `type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+ // place is optional
+ place?: 'home' | 'work'
+}>`
+
export const szan = `// Make input todos as readonly array
function completeAll(
todos: readonly Todo[]
@@ -724,6 +732,14 @@ export const tgvw = `const bar: Todo = {
done: true
}`
+export const umjt = `type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+ // Union Type: Can be either 'home' or 'work'
+ place: 'home' | 'work'
+}>`
+
export const uxlb = `function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring
todo.done = !todo.done
@@ -825,6 +841,13 @@ export const yxjg = `function toggleTodo(todo) {
}
}`
+export const yztr = `// How to update this to support place labels?
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>`
+
export const zswn = `// Will this compile?
const testTodo: CompletedTodo = {
id: 1,
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 3fbb1bc..1c9a88a 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -18,7 +18,7 @@ import RunButtonText from 'src/components/RunButtonText'
import CodeBlock from 'src/components/CodeBlock'
import BubbleQuotes from 'src/components/BubbleQuotes'
import ResultHighlight from 'src/components/ResultHighlight'
-import LocationLabel from 'src/components/LocationLabel'
+import PlaceLabel from 'src/components/PlaceLabel'
const compileSuccess = 'Compiled successfully!'
const section1 = 'Types, Read-only Properties, and Mapped Types'
@@ -1442,45 +1442,73 @@ const Page = () => (
color: 'darkGreen'
},
{
- title: <>New Feature: Location labels>,
+ title: <>New Feature: Place labels>,
content: (
<>
Let’s add a new feature to our todo app:{' '}
- Location labels.
+ Place labels.
- Each todo item can now optionally be labeled as{' '}
- or{' '}
- , like this:
+
+ Each todo item can now optionally be labeled as{' '}
+ or
+ {' '}
+ as shown below. People can use this feature to identify which
+ tasks need to be done at home, at work, or elsewhere.
- To represent this, we need to use a TypeScript feature called{' '}
+ To represent place labels, we can use a TypeScript feature called{' '}
union types. In TypeScript, you can use the
- syntax A | B to create a union type, which represents
- a type that’s{' '}
+ syntax A | B to create a union type,
+ which represents a type that’s{' '}
either A or B
@@ -1499,8 +1527,62 @@ const Page = () => (
}
/>
- In this case, we need to create a union of Todo and{' '}
- Separator like this:
+ In this case, because the place property can be
+ either 'home' or 'work', we can create a
+ union 'home' | 'work':
+
+ We briefly mentioned that place labels like{' '}
+ or are{' '}
+ optional—we can have todo items without a place
+ label. In our previous example, “Go to Gym”{' '}
+ didn’t have any place label:
+
+
+ Place labels are optional: “Go to Gym”{' '}
+ didn’t have any place label.
+ >
+ }
+ defaultData={[
+ { id: 1, text: 'Do laundry', done: false, place: 'home' },
+ { id: 2, text: 'Email boss', done: false, place: 'work' },
+ { id: 3, text: 'Go to gym', done: false }
+ ]}
+ shouldAlwaysHighlight={lineIndex => lineIndex === 13}
+ />
+
+ In TypeScript,{' '}
+
+ you can add a question mark (?) after a property
+ name to make the property optional:
+
+
+
+ lineIndex === 5 && tokenIndex <= 1
+ }
+ />
+
+ That’s it! The above Todo type will allow the{' '}
+ place property to be 'home',{' '}
+ 'work', or missing from the object.
Let’s add a new feature to our todo app:{' '}
- Place labels.
+ Place tags.{' '}
+
+ Each todo item can now optionally be tagged with one of the
+ following tags:
+
+
+
+ Home:
+
+
+ Work:
+
+
+ Custom place:{' '}
+ ,{' '}
+ , etc—the user
+ can create any custom place they want.
+
+
+ People can use this feature to identify which tasks need to be
+ done at home, at work, or elsewhere.{' '}
- Each todo item can now optionally be labeled as{' '}
- or
- {' '}
- as shown below. People can use this feature to identify which
- tasks need to be done at home, at work, or elsewhere.
+ It’s optional, so there can be a todo item
+ without a place tag.
+
+
Here’s an example:
- Each todo item can now optionally be labeled as{' '}
- or
+ Each todo item can now optionally be tagged with a{' '}
+ place tag
>
}
- defaultData={[
- { id: 1, text: 'Do laundry', done: false, place: 'home' },
- { id: 2, text: 'Email boss', done: false, place: 'work' },
- { id: 3, text: 'Buy milk', done: false }
- ]}
+ defaultData={placesExample}
/>
Let’s take a look at the associated data.{' '}
Each todo now can have an optional place property,
- which can be either 'home' or 'work'.
+ which can have the following value:
To implement this in TypeScript, we first need to update our
- definition of the Todo type. Let’s take a look at how
- we can do this.
+ definition of the Todo type. Let’s take a look at
+ this next!
(
content: (
<>
- To represent place labels, we can use a TypeScript feature called{' '}
- union types. In TypeScript, you can use the
- syntax A | B to create a union type,
- which represents a type that’s{' '}
+ To represent place tags, we can use a TypeScript feature called{' '}
+ union types. In TypeScript,{' '}
+ you can use the syntax A | B to create a{' '}
+ union type, which represents a type that’s{' '}
either A or B
.
@@ -1527,130 +1581,113 @@ const Page = () => (
}
/>
- In this case, because the place property can be
- either 'home' or 'work', we can create a
- union 'home' | 'work':
+ For example, if you create a type that’s equal to{' '}
+ number | string, it can be either number{' '}
+ OR string:
- We briefly mentioned that place labels like{' '}
- or are{' '}
- optional—we can have todo items without a place
- label. In our previous example, “Buy milk”{' '}
- didn’t have any place label:
+ In our todo app,{' '}
+
+ we’ll first create a new Place type as a union type
+ {' '}
+ as follows:
-
- Place labels are optional: “Buy milk”{' '}
- didn’t have any place label.
+ Place can be either 'home',{' '}
+ 'work', or an object containing a string{' '}
+ custom property
>
}
- defaultData={[
- { id: 1, text: 'Do laundry', done: false, place: 'home' },
- { id: 2, text: 'Email boss', done: false, place: 'work' },
- { id: 3, text: 'Buy milk', done: false }
- ]}
- shouldAlwaysHighlight={lineIndex => lineIndex === 13}
+ snippet={snippets.umjt}
+ shouldHighlight={(lineIndex, tokenIndex) =>
+ lineIndex === 0 && tokenIndex >= 6
+ }
/>
- In TypeScript,{' '}
-
- you can add a question mark (?) after a property
- name to make the property optional:
-
+ Then we can assign the Place type to the{' '}
+ place property of Todo:
- Now, let’s improve the place labels feature by allowing users to
- assign a custom place label for places other than{' '}
- or .
-
-
- In the following example, we’ve created new{' '}
- custom place labels called{' '}
- and{' '}
- :
+ We briefly mentioned that place tags like{' '}
+ or are{' '}
+ optional—we can have todo items without a place
+ tag. In our previous example, “Read a book”{' '}
+ didn’t have any place tag, so it didn’t have the{' '}
+ place property:
- Added custom places {' '}
- and
+ Place tags are optional: “Read a book”{' '}
+ didn’t have any place tag, so NO place property
>
}
- defaultData={[
- {
- id: 3,
- text: 'Go to gym',
- done: false,
- place: { custom: 'Gym' }
- },
- {
- id: 4,
- text: 'Read a book',
- done: false,
- place: { custom: 'Cafe' }
- }
- ]}
+ defaultData={placesExample}
+ shouldAlwaysHighlight={lineIndex => lineIndex === 6}
/>
- Here’s what the data looks like.{' '}
+ Can TypeScript describe these optional properties? Of
+ course it can. In TypeScript,{' '}
- For custom places, we’ll set the place property as
- an object like this: {`{ custom: 'Gym' }`}.
+ you can add a question mark (?)
+ after a property name to make the property optional:
+ We’d like to implement a function called{' '}
+ placeToString(), which has the following input and
+ output:
+
+
+
+ Parameter should be a Place.{' '}
+
+ Example: 'work'.
+
+
+
+ Return value should be a string (with an emoji)
+ that will be used for the label UI.{' '}
+
+ Example:{' '}
+
+ ' Work'
+
+ .
+
+
+
+
Here are more examples:
+
+
+ We can then use the return value of placeToString(){' '}
+ to render place label UIs: ,{' '}
+ ,{' '}
+ , etc. For example, if we
+ were to use React, we’d write a component like this:
+
+ If we were to use the above React component,{' '}
+ {``} will render{' '}
+ . (Don’t worry if you don’t know
+ React—the bottom line is that placeToString() is
+ useful for rendering the UI.)
+
- That’s it! We’re now ready to use this new Todo type
- in a function.
+ Let’s now implement placeToString(). Here’s the
+ starter code—can you figure out what goes inside?
- Parameter should be a Place.{' '}
+ Input should be a Place.{' '}
Example: 'work'.
@@ -1720,40 +1720,15 @@ const Page = () => (
-
Here are more examples:
+
Here are the examples:
We can then use the return value of placeToString(){' '}
to render place label UIs: ,{' '}
,{' '}
- , etc. For example, if we
- were to use React, we’d write a component like this:
+ , etc in any UI library
+ (e.g. React).
- If we were to use the above React component,{' '}
- {``} will render{' '}
- . (Don’t worry if you don’t know
- React—the bottom line is that placeToString() is
- useful for rendering the UI.)
-
- We’d like to implement a function called{' '}
- placeToString(), which has the following input and
- output:
+ As mentioned before, UI libraries like React or Vue{' '}
+ transform data into UI.
+
+ UI libraries transform data into UI>}
+ />
+
+ For place labels, we need to transform each Place{' '}
+ data into a place label UI:
+
+ To do this, we’d like to implement a function called{' '}
+
+ placeToString()
+
+ , which has the following input and output:
@@ -1709,8 +1788,9 @@ const Page = () => (
- Return value should be a string (with an emoji)
- that will be used for the label UI.{' '}
+ Return value should be a{' '}
+ string (with an emoji) that will be used for
+ the label UI.{' '}
Example:{' '}
From 5e615f5e7b01692630188de8263355fa1ff8c6cc Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Mon, 16 Dec 2019 09:26:38 +0900
Subject: [PATCH 29/57] Start slide 23
---
.prettierrc | 6 ++-
snippets/snippets/todo/vgja.ts | 10 +++++
snippets/snippets/todo/ybhj.ts | 4 +-
src/components/CodeBlockHighlight.tsx | 30 +++++++++++++++
src/components/CodeBlockPre.tsx | 2 +-
src/components/CodeResult.tsx | 2 +-
src/lib/snippets.ts | 17 +++++++--
src/pages/todo.tsx | 53 +++++++++++++++++++++------
8 files changed, 104 insertions(+), 20 deletions(-)
create mode 100644 snippets/snippets/todo/vgja.ts
diff --git a/.prettierrc b/.prettierrc
index 4765af0..9a5ea47 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -9,7 +9,11 @@
}
},
{
- "files": ["snippets/snippets/**/umjt.ts", "snippets/snippets/**/rvyq.ts"],
+ "files": [
+ "snippets/snippets/**/umjt.ts",
+ "snippets/snippets/**/rvyq.ts",
+ "snippets/snippets/**/vgja.ts"
+ ],
"options": {
"printWidth": 54
}
diff --git a/snippets/snippets/todo/vgja.ts b/snippets/snippets/todo/vgja.ts
new file mode 100644
index 0000000..5fd2340
--- /dev/null
+++ b/snippets/snippets/todo/vgja.ts
@@ -0,0 +1,10 @@
+type Place = 'home' | 'work' | { custom: string }
+
+// Little Duckling’s implementation
+function placeToString(place: Place): string {
+ if (place === 'home') {
+ return 'homeEmoji Home'
+ } else {
+ return 'pinEmoji ' + place.custom
+ }
+}
diff --git a/snippets/snippets/todo/ybhj.ts b/snippets/snippets/todo/ybhj.ts
index dee878e..2bf3049 100644
--- a/snippets/snippets/todo/ybhj.ts
+++ b/snippets/snippets/todo/ybhj.ts
@@ -1,4 +1,4 @@
-// Takes a Place and returns a string
function placeToString(place: Place): string {
- // Figure out what code goes here!
+ // Takes a Place and returns a string
+ // that can be used for the place label UI
}
diff --git a/src/components/CodeBlockHighlight.tsx b/src/components/CodeBlockHighlight.tsx
index 508c68e..7d00123 100644
--- a/src/components/CodeBlockHighlight.tsx
+++ b/src/components/CodeBlockHighlight.tsx
@@ -136,6 +136,36 @@ const CodeBlockHighlight = ({
>
)
}
+ if (/homeEmoji/.exec(children)) {
+ const splitChildren = children.split('homeEmoji')
+ updatedChildren = (
+ <>
+ {splitChildren[0]}
+
+ {splitChildren[1]}
+ >
+ )
+ }
+ if (/workEmoji/.exec(children)) {
+ const splitChildren = children.split('workEmoji')
+ updatedChildren = (
+ <>
+ {splitChildren[0]}
+
+ {splitChildren[1]}
+ >
+ )
+ }
+ if (/pinEmoji/.exec(children)) {
+ const splitChildren = children.split('pinEmoji')
+ updatedChildren = (
+ <>
+ {splitChildren[0]}
+
+ {splitChildren[1]}
+ >
+ )
+ }
return (
`
-export const ybhj = `// Takes a Place and returns a string
-function placeToString(place: Place): string {
- // Figure out what code goes here!
+export const ybhj = `function placeToString(place: Place): string {
+ // Takes a Place and returns a string
+ // that can be used for the place label UI
}`
export const yhto = `type Todo = {
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index aa749fd..2dfdcee 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -317,11 +317,11 @@ const Page = () => (
To implement the above functionality, we need to write code that
toggles the done property of a todo item. Let’s name
- this function toggleTodo.{' '}
+ this function toggleTodo().{' '}
- When you call toggleTodo on a todo object, it needs
- to return a new todo object with the opposite boolean value for
- the done property.
+ When you call toggleTodo() on a todo object, it
+ needs to return a new todo object with the opposite boolean
+ value for the done property.
@@ -342,7 +342,7 @@ const Page = () => (
/>
And it looks like Little Duckling has implemented{' '}
- toggleTodo for us:
+ toggleTodo() for us:
(
children: (
<>
- I’ve implemented toggleTodo for you. Could
- you take a look?
+ I’ve implemented toggleTodo() for you.
+ Could you take a look?
First,{' '}
- we specify that the input to toggleTodo must be{' '}
+ we specify that the input to toggleTodo() must be{' '}
Todo
. We do this by adding : Todo next to the parameter{' '}
@@ -548,8 +548,8 @@ const Page = () => (
Next,{' '}
- we specify that the return type of toggleTodo must
- also be Todo
+ we specify that the return type of toggleTodo(){' '}
+ must also be Todo
. We do this by adding : Todo after the parameter
list.
@@ -625,7 +625,7 @@ const Page = () => (
children: (
<>
- I think I can refactor toggleTodo as
+ I think I can refactor toggleTodo() as
follows. Could you take a look?
+ I tried to implement placeToString(). Could
+ you take a look?
+
+ >
+ )
+ }
+ ]}
+ />
+
+ Let’s see if Little Duckling’s implementation compiles.{' '}
+
+ Press !
+
+
+
+ >
+ )
+ },
underConstructionCard
]}
/>
From 1de18e5a5b66a47ff86b47b8d1af8625477da9e6 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Mon, 16 Dec 2019 09:29:45 +0900
Subject: [PATCH 30/57] Compile failure
---
src/pages/todo.tsx | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 2dfdcee..78d0753 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1842,7 +1842,16 @@ const Page = () => (
Press !
-
+
+ lineIndex === 7 && tokenIndex >= 7
+ }
+ />
>
)
},
From 62fb06e140fa8c733c3c913f526accb9646b318c Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Mon, 16 Dec 2019 10:44:13 +0900
Subject: [PATCH 31/57] Finish slide 23
---
.prettierrc | 6 ++-
snippets/snippets/todo/dhor.ts | 17 ++++++
snippets/snippets/todo/eega.ts | 5 ++
snippets/snippets/todo/szco.ts | 11 ++++
src/lib/snippets.ts | 36 +++++++++++++
src/lib/theme/letterSpacings.ts | 2 +-
src/pages/todo.tsx | 94 ++++++++++++++++++++++++++++++++-
7 files changed, 166 insertions(+), 5 deletions(-)
create mode 100644 snippets/snippets/todo/dhor.ts
create mode 100644 snippets/snippets/todo/eega.ts
create mode 100644 snippets/snippets/todo/szco.ts
diff --git a/.prettierrc b/.prettierrc
index 9a5ea47..989a400 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -12,10 +12,12 @@
"files": [
"snippets/snippets/**/umjt.ts",
"snippets/snippets/**/rvyq.ts",
- "snippets/snippets/**/vgja.ts"
+ "snippets/snippets/**/vgja.ts",
+ "snippets/snippets/**/dhor.ts",
+ "snippets/snippets/**/szco.ts"
],
"options": {
- "printWidth": 54
+ "printWidth": 53
}
}
]
diff --git a/snippets/snippets/todo/dhor.ts b/snippets/snippets/todo/dhor.ts
new file mode 100644
index 0000000..a94cbf0
--- /dev/null
+++ b/snippets/snippets/todo/dhor.ts
@@ -0,0 +1,17 @@
+type Place = 'home' | 'work' | { custom: string }
+
+// TypeScript knows what the type of place would be
+// at each point inside the function
+function placeToString(place: Place): string {
+ // In here, place = 'home', 'work' or { custom:… }
+
+ if (place === 'home') {
+ // In here, place = 'home'
+
+ return 'homeEmoji Home'
+ } else {
+ // In here, place = 'work' or { custom: string }
+
+ return 'pinEmoji ' + place.custom
+ }
+}
diff --git a/snippets/snippets/todo/eega.ts b/snippets/snippets/todo/eega.ts
new file mode 100644
index 0000000..081a813
--- /dev/null
+++ b/snippets/snippets/todo/eega.ts
@@ -0,0 +1,5 @@
+else {
+ // place = 'work' or { custom: string }, and
+ // place.custom is invalid if place = 'work'
+ return 'pinEmoji ' + place.custom
+}
\ No newline at end of file
diff --git a/snippets/snippets/todo/szco.ts b/snippets/snippets/todo/szco.ts
new file mode 100644
index 0000000..e2e94f4
--- /dev/null
+++ b/snippets/snippets/todo/szco.ts
@@ -0,0 +1,11 @@
+// Correct implementation
+function placeToString(place: Place): string {
+ if (place === 'home') {
+ return 'homeEmoji Home'
+ } else if (place === 'work') {
+ return 'workEmoji Work'
+ } else {
+ // place is guaranteed to be { custom: string }
+ return 'pinEmoji ' + place.custom
+ }
+}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 7f2f3a9..db975b2 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -495,6 +495,24 @@ function toggleTodo(todo: Todo) {
// ...
}`
+export const dhor = `type Place = 'home' | 'work' | { custom: string }
+
+// TypeScript knows what the type of place would be
+// at each point inside the function
+function placeToString(place: Place): string {
+ // In here, place = 'home', 'work' or { custom:… }
+
+ if (place === 'home') {
+ // In here, place = 'home'
+
+ return 'homeEmoji Home'
+ } else {
+ // In here, place = 'work' or { custom: string }
+
+ return 'pinEmoji ' + place.custom
+ }
+}`
+
export const dqwb = `function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring
todo.done = !todo.done
@@ -508,6 +526,12 @@ export const dxfc = `// Associated data. If we're using React, this
{ id: 2, text: 'Second todo', done: false }
]`
+export const eega = `else {
+ // place = 'work' or { custom: string }, and
+ // place.custom is invalid if place = 'work'
+ return 'pinEmoji ' + place.custom
+}`
+
export const frtm = `type Todo = {
id: number
text: string
@@ -759,6 +783,18 @@ function completeAll(
// ...
}`
+export const szco = `// Correct implementation
+function placeToString(place: Place): string {
+ if (place === 'home') {
+ return 'homeEmoji Home'
+ } else if (place === 'work') {
+ return 'workEmoji Work'
+ } else {
+ // place is guaranteed to be { custom: string }
+ return 'pinEmoji ' + place.custom
+ }
+}`
+
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/lib/theme/letterSpacings.ts b/src/lib/theme/letterSpacings.ts
index a73d8c7..ec10a97 100644
--- a/src/lib/theme/letterSpacings.ts
+++ b/src/lib/theme/letterSpacings.ts
@@ -1,7 +1,7 @@
export const allLetterSpacings = {
title: '-0.02em',
wide: '0.15em',
- smallCode: '-0.025em'
+ smallCode: '-0.035em'
}
const letterSpacings = (x: keyof typeof allLetterSpacings) =>
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 78d0753..5df90c3 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -414,8 +414,8 @@ const Page = () => (
]}
/>
- Here’s the correct implementation. It preserves the{' '}
- id property.
+ No worries, Little Duckling! Here’s the correct implementation. It
+ preserves the id property.
(
lineIndex === 7 && tokenIndex >= 7
}
/>
+
+ It failed! TypeScript noticed that there’s a
+ logic error here.{' '}
+
+ Specifically, inside else, TypeScript knows that{' '}
+ place is either 'work' or{' '}
+ {`{ custom: string }`}
+
+ :
+
+ Because place is either 'work' or{' '}
+ {`{ custom: string }`} inside else, and{' '}
+
+ you can’t do place.custom if place is{' '}
+ 'work', TypeScript gives you a compile error.
+
+
+ lineIndex === 2}
+ />
+
+ Of course, the fix is to add{' '}
+ else if (place === 'work').{' '}
+
+ Press !
+
+
+ Oops! I forgot to check for{' '}
+ place === 'work'!
+
+ >
+ )
+ }
+ ]}
+ />
+
+ No worries, Little Duckling! TypeScript was able to catch the
+ error early.
+
+
+ As we just saw,{' '}
+
+ union types are powerful when combined with conditional
+ statements (e.g. if/else)
+
+ :
+
+
+
+ If we have a variable that’s a union type (e.g.{' '}
+ place)…
+
+
+ And check for its value in if/else…
+
+
+ Then TypeScript is smart about what the variable’s possible
+ values are for each branch of if/else.
+
+
+
+ That’s everything! Let’s quickly summarize what we’ve learned.
+
>
)
},
From 65b4865f04a01d60683eb79531732c4cd652d551 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 06:10:23 +0900
Subject: [PATCH 32/57] Update slide 23
---
.prettierrc | 8 +-
snippets/bin/generateSnippetsBundle.ts | 1 +
.../snippets/todo/{ => longerWidth}/dhor.ts | 4 +-
snippets/snippets/todo/longerWidth/ntup.ts | 18 +++
.../snippets/todo/{ => longerWidth}/rvyq.ts | 0
.../snippets/todo/{ => longerWidth}/szco.ts | 0
.../snippets/todo/{ => longerWidth}/umjt.ts | 0
.../snippets/todo/{ => longerWidth}/vgja.ts | 0
src/lib/snippets.ts | 125 ++++++++++--------
src/pages/todo.tsx | 7 +
10 files changed, 101 insertions(+), 62 deletions(-)
rename snippets/snippets/todo/{ => longerWidth}/dhor.ts (79%)
create mode 100644 snippets/snippets/todo/longerWidth/ntup.ts
rename snippets/snippets/todo/{ => longerWidth}/rvyq.ts (100%)
rename snippets/snippets/todo/{ => longerWidth}/szco.ts (100%)
rename snippets/snippets/todo/{ => longerWidth}/umjt.ts (100%)
rename snippets/snippets/todo/{ => longerWidth}/vgja.ts (100%)
diff --git a/.prettierrc b/.prettierrc
index 989a400..3b59ede 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -9,13 +9,7 @@
}
},
{
- "files": [
- "snippets/snippets/**/umjt.ts",
- "snippets/snippets/**/rvyq.ts",
- "snippets/snippets/**/vgja.ts",
- "snippets/snippets/**/dhor.ts",
- "snippets/snippets/**/szco.ts"
- ],
+ "files": ["snippets/snippets/**/longerWidth/*.ts"],
"options": {
"printWidth": 53
}
diff --git a/snippets/bin/generateSnippetsBundle.ts b/snippets/bin/generateSnippetsBundle.ts
index 1d9488b..149394a 100644
--- a/snippets/bin/generateSnippetsBundle.ts
+++ b/snippets/bin/generateSnippetsBundle.ts
@@ -9,6 +9,7 @@ const regenerate = () => {
const contents = fs.readFileSync(file, 'utf8')
return `export const ${file
.replace(/\.\/snippets\/snippets\/\w+\//, '')
+ .replace(/longerWidth\//, '')
.replace(/\.ts/, '')} = \`${contents
.trim()
.replace(/^;/m, '')
diff --git a/snippets/snippets/todo/dhor.ts b/snippets/snippets/todo/longerWidth/dhor.ts
similarity index 79%
rename from snippets/snippets/todo/dhor.ts
rename to snippets/snippets/todo/longerWidth/dhor.ts
index a94cbf0..ad62032 100644
--- a/snippets/snippets/todo/dhor.ts
+++ b/snippets/snippets/todo/longerWidth/dhor.ts
@@ -1,7 +1,7 @@
type Place = 'home' | 'work' | { custom: string }
-// TypeScript knows what the type of place would be
-// at each point inside the function
+// TypeScript knows what the type of "place"
+// would be at each point inside the function
function placeToString(place: Place): string {
// In here, place = 'home', 'work' or { custom:… }
diff --git a/snippets/snippets/todo/longerWidth/ntup.ts b/snippets/snippets/todo/longerWidth/ntup.ts
new file mode 100644
index 0000000..ec0d6e6
--- /dev/null
+++ b/snippets/snippets/todo/longerWidth/ntup.ts
@@ -0,0 +1,18 @@
+// If we have a variable that’s a union type…
+type Place = 'home' | 'work' | { custom: string }
+
+function placeToString(place: Place): string {
+ // TypeScript is smart about what the variable’s
+ // possible values are for each branch of if/else
+
+ if (place === 'home') {
+ // TypeScript knows that place = 'home' here
+ // (So it won’t compile if you do place.custom)
+ } else if (place === 'work') {
+ // TypeScript knows that place = 'work' here
+ // (So it won’t compile if you do place.custom)
+ } else {
+ // TypeScript knows place = { custom: … } here
+ // (So you can do place.custom)
+ }
+}
diff --git a/snippets/snippets/todo/rvyq.ts b/snippets/snippets/todo/longerWidth/rvyq.ts
similarity index 100%
rename from snippets/snippets/todo/rvyq.ts
rename to snippets/snippets/todo/longerWidth/rvyq.ts
diff --git a/snippets/snippets/todo/szco.ts b/snippets/snippets/todo/longerWidth/szco.ts
similarity index 100%
rename from snippets/snippets/todo/szco.ts
rename to snippets/snippets/todo/longerWidth/szco.ts
diff --git a/snippets/snippets/todo/umjt.ts b/snippets/snippets/todo/longerWidth/umjt.ts
similarity index 100%
rename from snippets/snippets/todo/umjt.ts
rename to snippets/snippets/todo/longerWidth/umjt.ts
diff --git a/snippets/snippets/todo/vgja.ts b/snippets/snippets/todo/longerWidth/vgja.ts
similarity index 100%
rename from snippets/snippets/todo/vgja.ts
rename to snippets/snippets/todo/longerWidth/vgja.ts
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index db975b2..b10e66b 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -495,24 +495,6 @@ function toggleTodo(todo: Todo) {
// ...
}`
-export const dhor = `type Place = 'home' | 'work' | { custom: string }
-
-// TypeScript knows what the type of place would be
-// at each point inside the function
-function placeToString(place: Place): string {
- // In here, place = 'home', 'work' or { custom:… }
-
- if (place === 'home') {
- // In here, place = 'home'
-
- return 'homeEmoji Home'
- } else {
- // In here, place = 'work' or { custom: string }
-
- return 'pinEmoji ' + place.custom
- }
-}`
-
export const dqwb = `function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring
todo.done = !todo.done
@@ -601,6 +583,78 @@ export const lieq = `type Todo = {
done: boolean
}`
+export const dhor = `type Place = 'home' | 'work' | { custom: string }
+
+// TypeScript knows what the type of "place"
+// would be at each point inside the function
+function placeToString(place: Place): string {
+ // In here, place = 'home', 'work' or { custom:… }
+
+ if (place === 'home') {
+ // In here, place = 'home'
+
+ return 'homeEmoji Home'
+ } else {
+ // In here, place = 'work' or { custom: string }
+
+ return 'pinEmoji ' + place.custom
+ }
+}`
+
+export const ntup = `// If we have a variable that’s a union type…
+type Place = 'home' | 'work' | { custom: string }
+
+function placeToString(place: Place): string {
+ // TypeScript is smart about what the variable’s
+ // possible values are for each branch of if/else
+
+ if (place === 'home') {
+ // TypeScript knows that place = 'home' here
+ // (So it won’t compile if you do place.custom)
+ } else if (place === 'work') {
+ // TypeScript knows that place = 'work' here
+ // (So it won’t compile if you do place.custom)
+ } else {
+ // TypeScript knows place = { custom: … } here
+ // (So you can do place.custom)
+ }
+}`
+
+export const rvyq = `type Place = 'home' | 'work' | { custom: string }
+
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+ // place is optional
+ place?: Place
+}>`
+
+export const szco = `// Correct implementation
+function placeToString(place: Place): string {
+ if (place === 'home') {
+ return 'homeEmoji Home'
+ } else if (place === 'work') {
+ return 'workEmoji Work'
+ } else {
+ // place is guaranteed to be { custom: string }
+ return 'pinEmoji ' + place.custom
+ }
+}`
+
+export const umjt = `type Place = 'home' | 'work' | { custom: string }`
+
+export const vgja = `type Place = 'home' | 'work' | { custom: string }
+
+// Little Duckling’s implementation
+function placeToString(place: Place): string {
+ if (place === 'home') {
+ return 'homeEmoji Home'
+ } else {
+ return 'pinEmoji ' + place.custom
+ }
+}`
+
export const lund = `const result = toggleTodo({
id: 1,
text: '…',
@@ -766,16 +820,6 @@ export const ruga = `function completeAll(
// ...
}`
-export const rvyq = `type Place = 'home' | 'work' | { custom: string }
-
-type Todo = Readonly<{
- id: number
- text: string
- done: boolean
- // place is optional
- place?: Place
-}>`
-
export const szan = `// Make input todos as readonly array
function completeAll(
todos: readonly Todo[]
@@ -783,18 +827,6 @@ function completeAll(
// ...
}`
-export const szco = `// Correct implementation
-function placeToString(place: Place): string {
- if (place === 'home') {
- return 'homeEmoji Home'
- } else if (place === 'work') {
- return 'workEmoji Work'
- } else {
- // place is guaranteed to be { custom: string }
- return 'pinEmoji ' + place.custom
- }
-}`
-
export const tdbp = `// Takes an array of todo items and returns
// a new array where "done" is all true
function completeAll(todos) {
@@ -806,25 +838,12 @@ export const tgvw = `const bar: Todo = {
done: true
}`
-export const umjt = `type Place = 'home' | 'work' | { custom: string }`
-
export const uxlb = `function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring
todo.done = !todo.done
return todo
}`
-export const vgja = `type Place = 'home' | 'work' | { custom: string }
-
-// Little Duckling’s implementation
-function placeToString(place: Place): string {
- if (place === 'home') {
- return 'homeEmoji Home'
- } else {
- return 'pinEmoji ' + place.custom
- }
-}`
-
export const vgnq = `// This will continue to work because
// the input todo is not modified
function toggleTodo(todo: Todo): Todo {
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 5df90c3..7e0f2d4 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1939,6 +1939,13 @@ const Page = () => (
values are for each branch of if/else.
That’s everything! Let’s quickly summarize what we’ve learned.
From 1a6bbac314e22354c1c93bb269789cd2fdd099ea Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 07:28:45 +0900
Subject: [PATCH 33/57] Simplify
---
snippets/snippets/todo/longerWidth/ntup.ts | 4 ++--
src/lib/snippets.ts | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/snippets/snippets/todo/longerWidth/ntup.ts b/snippets/snippets/todo/longerWidth/ntup.ts
index ec0d6e6..5df8eb0 100644
--- a/snippets/snippets/todo/longerWidth/ntup.ts
+++ b/snippets/snippets/todo/longerWidth/ntup.ts
@@ -6,10 +6,10 @@ function placeToString(place: Place): string {
// possible values are for each branch of if/else
if (place === 'home') {
- // TypeScript knows that place = 'home' here
+ // TypeScript knows place = 'home' here
// (So it won’t compile if you do place.custom)
} else if (place === 'work') {
- // TypeScript knows that place = 'work' here
+ // TypeScript knows place = 'work' here
// (So it won’t compile if you do place.custom)
} else {
// TypeScript knows place = { custom: … } here
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index b10e66b..dd7d8bd 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -609,10 +609,10 @@ function placeToString(place: Place): string {
// possible values are for each branch of if/else
if (place === 'home') {
- // TypeScript knows that place = 'home' here
+ // TypeScript knows place = 'home' here
// (So it won’t compile if you do place.custom)
} else if (place === 'work') {
- // TypeScript knows that place = 'work' here
+ // TypeScript knows place = 'work' here
// (So it won’t compile if you do place.custom)
} else {
// TypeScript knows place = { custom: … } here
From ae5a4e551fcbcacbb502add61ebe03cc64650e67 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 07:52:56 +0900
Subject: [PATCH 34/57] Finish slide 24
---
snippets/snippets/todo/npog.ts | 7 +++
src/lib/snippets.ts | 8 +++
src/pages/todo.tsx | 100 +++++++++++++++++++++++++++------
3 files changed, 98 insertions(+), 17 deletions(-)
create mode 100644 snippets/snippets/todo/npog.ts
diff --git a/snippets/snippets/todo/npog.ts b/snippets/snippets/todo/npog.ts
new file mode 100644
index 0000000..a5a98e5
--- /dev/null
+++ b/snippets/snippets/todo/npog.ts
@@ -0,0 +1,7 @@
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+ // place is optional
+ place?: Place
+}>
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index dd7d8bd..d18956c 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -715,6 +715,14 @@ export const npgx = `type Todo = Readonly<{
place: Place
}>`
+export const npog = `type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+ // place is optional
+ place?: Place
+}>`
+
export const ntau = `function toggleTodo(todo: Todo): Todo {
// Little Duckling’s code from earlier:
// Missing the "id" property
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 7e0f2d4..1a05adb 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -47,6 +47,31 @@ const placesExample: Todo[] = [
}
]
+const UnionTypesSummary = () => (
+ <>
+
+
+ If we have a variable that’s a union type (e.g.{' '}
+ place)…
+
+
+ And check for its value in if/else…
+
+
+ Then TypeScript is smart about what the variable’s possible values are
+ for each branch of if/else.
+
+
-
- If we have a variable that’s a union type (e.g.{' '}
- place)…
-
-
- And check for its value in if/else…
-
-
- Then TypeScript is smart about what the variable’s possible
- values are for each branch of if/else.
-
-
+
+
+ That’s everything! Let’s quickly summarize what we’ve learned.
+
+ In this section, we’ve learned about union types and optional
+ properties:
+
+
+
+ 1. We can use the syntax A | B to create a{' '}
+ union type, which represents a type that’s
+ either A or B.
+
+
- lineIndex === 8 || lineIndex === 11 || lineIndex === 14
+ caption={
+ <>
+ Place can be either 'home',{' '}
+ 'work', or an object containing a string{' '}
+ custom property
+ >
+ }
+ snippet={snippets.umjt}
+ shouldHighlight={(lineIndex, tokenIndex) =>
+ lineIndex === 0 && tokenIndex >= 6
}
/>
- That’s everything! Let’s quickly summarize what we’ve learned.
+
+ 2. We can add a question mark (?) after a property
+ name to make the property optional.
+
+
+
+ lineIndex === 5 && tokenIndex <= 1
+ }
+ />
+
+ Finally,{' '}
+
+ union types are powerful when combined with conditional
+ statements (e.g. if/else).
+
+
+
+
+ Union types are one of the best ideas of TypeScript. You should
+ use them widely. There are other powerful features of union types
+ (discriminated unions, usage with mapped types, etc) which I won’t
+ cover here.
+ About the author: I’m Shu Uesugi, a software engineer.
+ The most recent TypeScript project I worked on is an{' '}
+ interactive computer science course called{' '}
+
+ “Y Combinator for Non-programmers”
+
+ .
+
+
+ I’m currently looking for a full-time opportunity:{' '}
+ Looking for a remote (PST timezone) position in full-stack engineering,
+ product management, or DevRel. You can email me at{' '}
+ shu@chibicode.com.
+
+ >
+)
+
+export default AboutMe
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index b0efa88..ad63458 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -3,6 +3,7 @@ import { css, jsx } from '@emotion/core'
import Container from 'src/components/Container'
import useTheme from 'src/hooks/useTheme'
import { A } from 'src/components/ContentTags'
+import GitHubButton from 'src/components/GitHubButton'
const Footer = () => {
const { fontSizes, ns, spaces, colors } = useTheme()
@@ -11,7 +12,6 @@ const Footer = () => {
{
}
`}
>
- The source code for this site is{' '}
+ Source available on{' '}
- available on GitHub
+ GitHub
- .
+ :
)
}
diff --git a/src/components/GitHubButton.tsx b/src/components/GitHubButton.tsx
new file mode 100644
index 0000000..b4ec06f
--- /dev/null
+++ b/src/components/GitHubButton.tsx
@@ -0,0 +1,34 @@
+/** @jsx jsx */
+import { css, jsx } from '@emotion/core'
+import { A } from 'src/components/ContentTags'
+import useTheme from 'src/hooks/useTheme'
+
+export const SourceAvailableText = () => (
+ <>
+ The source code for this site is on{' '}
+
+ GitHub
+
+ :
+ >
+)
+
+const GitHubButton = () => {
+ const { spaces } = useTheme()
+ return (
+
+ )
+}
+
+export default GitHubButton
diff --git a/src/components/InternalLink.tsx b/src/components/InternalLink.tsx
index 0132621..657faae 100644
--- a/src/components/InternalLink.tsx
+++ b/src/components/InternalLink.tsx
@@ -10,7 +10,7 @@ const InternalLink = ({
...props
}: JSX.IntrinsicElements['a'] & {
href: string
- cssOverrides: Interpolation
+ cssOverrides?: Interpolation
}) => {
const { colors } = useTheme()
return (
diff --git a/src/components/PostPage.tsx b/src/components/PostPage.tsx
index b7509c4..3f1e619 100644
--- a/src/components/PostPage.tsx
+++ b/src/components/PostPage.tsx
@@ -94,7 +94,7 @@ const PostPage = ({
backgroundColor: 'pink',
children: (
<>
-
+
>
)
}
diff --git a/src/lib/articles.ts b/src/lib/articles.ts
index f5ebd95..8e6b1f2 100644
--- a/src/lib/articles.ts
+++ b/src/lib/articles.ts
@@ -8,7 +8,7 @@ export const articlesData = {
todo: {
title:
'TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App',
- date: DateTime.fromISO('2019-12-16T12:00:00Z'),
+ date: DateTime.fromISO('2019-12-17T12:00:00Z'),
description: 'Learn TypeScript by Building a Todo App',
ogImage: 'todo'
},
diff --git a/src/pages/_error.tsx b/src/pages/_error.tsx
index 09c9bc4..a5ee212 100644
--- a/src/pages/_error.tsx
+++ b/src/pages/_error.tsx
@@ -4,6 +4,7 @@ import { P } from 'src/components/ContentTags'
import { siteTitle, siteOgImage } from 'src/lib/meta'
import Head from 'next/head'
import Page from 'src/components/Page'
+import ArticleList from 'src/components/ArticleList'
const PageNotFound = () => {
return (
@@ -22,6 +23,7 @@ const PageNotFound = () => {
quotes={[
{
type: 'bird',
+ backgroundColor: 'lightYellow1',
children: (
<>
@@ -33,6 +35,7 @@ const PageNotFound = () => {
}
]}
/>
+
)
}
diff --git a/src/pages/generics.tsx b/src/pages/generics.tsx
index 0a30127..904ba05 100644
--- a/src/pages/generics.tsx
+++ b/src/pages/generics.tsx
@@ -11,6 +11,8 @@ import Emoji from 'src/components/Emoji'
import { articlesData } from 'src/lib/articles'
import { baseUrl } from 'src/lib/meta'
import ReadMore from 'src/components/ReadMore'
+import AboutMe from 'src/components/AboutMe'
+import { SourceAvailableText } from 'src/components/GitHubButton'
const Page = () => (
(
you might know someone (maybe one of your Twitter followers)
who’s struggling with generics
- . If you do, I’d appreciate it if you could share this article
- with them. You can{' '}
+ . I’d appreciate it if you could share this article with them.
+ You can{' '}
(
click here to tweet this article.
- About the author: I’m Shu Uesugi, a software
- engineer. The most recent TypeScript project I worked on is an{' '}
- interactive computer science course called{' '}
-
- “Y Combinator for Non-programmers”
-
- .
-
- >
- )
+ content:
}
}
]}
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 44bde0a..8e682bd 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -4,36 +4,22 @@ import { P } from 'src/components/ContentTags'
import { siteTitle, baseUrl, siteDescription, siteOgImage } from 'src/lib/meta'
import Head from 'next/head'
import Page from 'src/components/Page'
-import ReadMore from 'src/components/ReadMore'
import ArticleList from 'src/components/ArticleList'
-export const FirstParagraph = ({
- defaultVisible
-}: {
- defaultVisible?: boolean
-}) => (
+export const FirstParagraph = () => (
<>
-
- Why target beginner programmers? As TypeScript is
- becoming popular, I believe that more beginner programmers (people
- with only a few months of coding experience) will be learning it,{' '}
- possibly as one of their first languages. So I wanted to
- create tutorials specifically targeting beginner programmers.
-
- }
- readMoreText="Read more…"
- preview={readMore => (
-
- Hello! I write tutorials to help{' '}
- beginner programmers learn TypeScript. My tutorials might NOT
- be as useful for experienced programmers learning TypeScript.
- {readMore}
-
- )}
- defaultVisible={defaultVisible}
- />
+
+ Hello! I write tutorials to help{' '}
+ beginner programmers learn TypeScript. My tutorials might NOT be
+ as useful for experienced programmers learning TypeScript.
+
+
+ Why target beginner programmers? As TypeScript is
+ becoming popular, I believe that more beginner programmers will be
+ learning it. However, I noticed that many existing TypeScript tutorials
+ are too challenging for beginner programmers. I wanted to offer a
+ beginner-friendly alternative here.
+
>
)
@@ -59,7 +45,7 @@ const Index = () => {
backgroundColor: 'lightYellow1',
children: (
<>
-
+
>
)
}
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 1a05adb..897c691 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -13,13 +13,18 @@ import {
UlLi
} from 'src/components/ContentTags'
import * as snippets from 'src/lib/snippets'
-import underConstructionCard from 'src/lib/underConstructionCard'
import TodoWithData, { Todo } from 'src/components/TodoWithData'
import RunButtonText from 'src/components/RunButtonText'
import CodeBlock from 'src/components/CodeBlock'
import BubbleQuotes from 'src/components/BubbleQuotes'
import ResultHighlight from 'src/components/ResultHighlight'
import PlaceLabel from 'src/components/PlaceLabel'
+import InternalLink from 'src/components/InternalLink'
+import AboutMe from 'src/components/AboutMe'
+import { SourceAvailableText } from 'src/components/GitHubButton'
+import TwitterLink from 'src/components/TwitterLink'
+import { articlesData } from 'src/lib/articles'
+import { baseUrl } from 'src/lib/meta'
const compileSuccess = 'Compiled successfully!'
const section1 = 'Types, Read-only Properties, and Mapped Types'
@@ -222,7 +227,32 @@ const Page = () => (
Let’s get started!
>
- )
+ ),
+ footer: {
+ content: (
+ <>
+
+ Note: If you already know TypeScript basics,
+ you won’t find anything new in this tutorial. However,{' '}
+
+ you might know someone (maybe one of your Twitter followers)
+ who’re interested in learning TypeScript
+
+ . I’d appreciate it if you could share this article with them.
+ You can{' '}
+
+ click here to tweet this article.
+
+
+
+
+
+ >
+ )
+ }
},
{
title: (
@@ -1947,7 +1977,7 @@ const Page = () => (
As we just saw,{' '}
union types are powerful when combined with conditional
- statements (e.g. if/else)
+ statements (e.g. if/else or switch)
:
@@ -2005,20 +2035,65 @@ const Page = () => (
Finally,{' '}
union types are powerful when combined with conditional
- statements (e.g. if/else).
+ statements (e.g. if/else or switch).
Union types are one of the best ideas of TypeScript. You should
use them widely. There are other powerful features of union types
- (discriminated unions, usage with mapped types, etc) which I won’t
- cover here.
+ (discriminated unions, combining them with mapped types, etc)
+ which I won’t cover here.
>
)
},
- underConstructionCard
+ {
+ title: <>Conclusion and next steps>,
+ content: (
+ <>
+
+
+ Thanks for reading! You should now know enough TypeScript to get
+ started with a project.
+
+
+
+ If you’re using React,{' '}
+
+ React+TypeScript Cheatsheets
+ {' '}
+ is a good reference.
+
+
+ Once you become more familiar with TypeScript, you should learn{' '}
+ generics next. I’ve written an article on it
+ called “
+
+ TypeScript Generics for People Who Gave Up on Understanding
+ Generics
+
+ ”.
+
+
+
+
+
+ I plan to write more TypeScript articles by building on the todo
+ app example I used here. To get notified when I publish a new
+ article, follow me on{' '}
+
+ Twitter at @chibicode
+
+ .
+
+
That’s why Little Duckling’s refactoring is a bad refactor—even
- thought it compiles correctly.
+ though it compiles correctly.
(
tests in TypeScript!)
- This especially useful{' '}
+ This is especially useful{' '}
when you’re using a UI library and need to transform data
@@ -1325,7 +1325,7 @@ const Page = () => (
}
/>
- We can de-duplicate the code by using a TypeScript feature called{' '}
+ We can deduplicate the code by using a TypeScript feature called{' '}
intersection types.
@@ -1579,19 +1579,19 @@ const Page = () => (
Let’s take a look at the associated data.{' '}
Each todo now can have an optional place property,
- which can have the following value:
+ which can have one of the following values:
@@ -1641,7 +1641,7 @@ const Page = () => (
emojis={['a', 'verticalBar', 'b']}
description={
<>
- A | B is an union type, which
+ A | B is a union type, which
means{' '}
either A or B.
@@ -2041,7 +2041,7 @@ const Page = () => (
Union types are one of the best ideas of TypeScript. You should
- use them widely. There are other powerful features of union types
+ use them often. There are other powerful features of union types
(discriminated unions, combining them with mapped types, etc)
which I won’t cover here.
Why target beginner programmers? As TypeScript is
- becoming popular, I believe that more beginner programmers will be
- learning it. However, I noticed that many existing TypeScript tutorials
- are too challenging for beginner programmers. I wanted to offer a
- beginner-friendly alternative here.
+ becoming popular, more beginner programmers will be learning it. However,
+ I noticed that many existing TypeScript tutorials are too hard for
+ beginner programmers. I wanted to offer a friendlier alternative.
Why target beginner programmers? As TypeScript is
becoming popular, more beginner programmers will be learning it. However,
- I noticed that many existing TypeScript tutorials are too hard for
- beginner programmers. I wanted to offer a friendlier alternative.
+ I noticed that many existing tutorials are not so friendly for beginner
+ programmers. This is my attempt to offer a friendlier alternative.
>
From 1c908f07e004a4bae1d62d75c84d5a37e7490144 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 20:09:47 +0900
Subject: [PATCH 43/57] Wordsmith slide 4
---
snippets/snippets/todo/vpco.ts | 5 ++++-
src/lib/snippets.ts | 5 ++++-
src/pages/todo.tsx | 33 ++++++++++++++++-----------------
3 files changed, 24 insertions(+), 19 deletions(-)
diff --git a/snippets/snippets/todo/vpco.ts b/snippets/snippets/todo/vpco.ts
index 0c7981d..0961ef9 100644
--- a/snippets/snippets/todo/vpco.ts
+++ b/snippets/snippets/todo/vpco.ts
@@ -1,9 +1,12 @@
-// Returns a new todo object with the opposite
+// Takes a single todo object and returns
+// a new todo object containing the opposite
// boolean value for the "done" proprty.
function toggleTodo(todo) {
// ...
}
+// Example usage:
+
toggleTodo({ id: …, text: '…', done: true })
// -> returns { id: …, text: '…', done: false }
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 7395e2f..ff5f441 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -862,12 +862,15 @@ function toggleTodo(todo: Todo): Todo {
}
}`
-export const vpco = `// Returns a new todo object with the opposite
+export const vpco = `// Takes a single todo object and returns
+// a new todo object containing the opposite
// boolean value for the "done" proprty.
function toggleTodo(todo) {
// ...
}
+// Example usage:
+
toggleTodo({ id: …, text: '…', done: true })
// -> returns { id: …, text: '…', done: false }
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 4d73f5e..dece483 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -298,6 +298,7 @@ const Page = () => (
+
Here’s what’s inside each todo object:
@@ -379,7 +380,8 @@ const Page = () => (
<>
To implement the check/uncheck functionality, we need to write
- code that toggles the done property of a todo item.
+ code that toggles the done property of a single todo
+ item.
Let’s name this function{' '}
@@ -391,7 +393,8 @@ const Page = () => (
- When you call toggleTodo() on a todo object…
+ When you call toggleTodo() on a single todo
+ object…
@@ -404,8 +407,8 @@ const Page = () => (
Now, let me introduce our junior developer,{' '}
- Little Duckling. We’re going to use some
- characters like him to make this article a bit more entertaining.
+ Little Duckling. He’s going to implement{' '}
+ toggleTodo() for us.
(
>
}
/>
-
- And it looks like Little Duckling has implemented{' '}
- toggleTodo() for us:
-
(
Let’s check if Little Duckling’s implementation is correct.{' '}
Take a look at the following test case. What do you think the
- output would be? Try to guess first and press Press{' '}
- below.
+ output would be? Try to guess first and press {' '}
+ below.
(
]}
/>
- No worries, Little Duckling! Here’s the correct implementation. It
- preserves the id property.
+ No worries, Little Duckling! Here’s the correct implementation:
(
/>
Now, here’s a question:{' '}
- How can we prevent mistakes like this?{' '}
- Little Duckling is a junior developer, but we want to make sure
- that he succeeds at his job by helping him make fewer mistakes.
+
+ How can we prevent Little Duckling from making mistakes like
+ this?
+ {' '}
+ We want to help Little Duckling succeed at his job!
(
}
/>
- This is where TypeScript comes in. Let’s take a
- look!
+ This is where TypeScript comes in.
- By using TypeScript, we can prevent the mistake
- Little Duckling made.
+ By using TypeScript, we can prevent the mistake Little Duckling
+ made by doing something called type checking.
- First, we create a type for the data we
- use. In this case, we need to create a type for a todo item. We’ll
- call it Todo and define using the following
- TypeScript syntax:
+ First,{' '}
+
+ we create a type
+ {' '}
+ for the data we use. In our case, we need to create a type for a
+ todo item.{' '}
+
+ We’ll call this type Todo and define it using the
+ following TypeScript syntax
+
+ :
- We can then use this type to check if an object is indeed a todo
- item. The TypeScript syntax for that is:{' '}
- variableName: TypeName (highlighted below). Let’s try
- it—
+ We can then use this type to{' '}
+ check if a variable is indeed a todo item.
+ The TypeScript syntax to do this check is:{' '}
+ variableName: Todo. Here’s an example below—
- press .
+ press
- This one failed to compile because the id property
- was missing.{' '}
+ This one failed to compile{' '}
+
+ because the id property was missing
+
+ .
+
+
+ Finally, how about this one?{' '}
+
+ Try pressing .
+
+
+ lineIndex === 1}
+ compile
+ resultError
+ result={
+ <>{`Type 'string' is not assignable to type 'number'.`}>
+ }
+ />
+
+ This one also failed to compile{' '}
- TypeScript can catch these small mistakes quickly, well before
- running the code—without having to write unit tests.
+ because the id property was string{' '}
+ instead of number, as defined on the{' '}
+ Todo type:
+ lineIndex === 1}
+ />
+
+ The bottom line is that TypeScript lets you type check a variable
+ against a specified type, which helps you catch mistakes early.
+
>
)
},
From 9a90a99aad395946fdf8152dc1cdb2f81e764f47 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 20:47:29 +0900
Subject: [PATCH 45/57] Wordsmith slide 6
---
snippets/snippets/todo/csum.ts | 2 +-
snippets/snippets/todo/irmt.ts | 5 ----
src/lib/snippets.ts | 8 +-----
src/pages/todo.tsx | 46 ++++++++--------------------------
4 files changed, 12 insertions(+), 49 deletions(-)
delete mode 100644 snippets/snippets/todo/irmt.ts
diff --git a/snippets/snippets/todo/csum.ts b/snippets/snippets/todo/csum.ts
index 95aae08..9106c82 100644
--- a/snippets/snippets/todo/csum.ts
+++ b/snippets/snippets/todo/csum.ts
@@ -1,4 +1,4 @@
-// todo must match the Todo type
+// Parameter "todo" must match the Todo type
function toggleTodo(todo: Todo) {
// ...
}
diff --git a/snippets/snippets/todo/irmt.ts b/snippets/snippets/todo/irmt.ts
deleted file mode 100644
index 491bf2c..0000000
--- a/snippets/snippets/todo/irmt.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-const baz: Todo = {
- id: 'abc',
- text: '…',
- done: true
-}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 9cae353..9672a68 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -490,7 +490,7 @@ export const bpmz = `type CompletedTodo = Readonly<{
done: true
}>`
-export const csum = `// todo must match the Todo type
+export const csum = `// Parameter "todo" must match the Todo type
function toggleTodo(todo: Todo) {
// ...
}`
@@ -544,12 +544,6 @@ export const hszk = `function completeAll(
}))
}`
-export const irmt = `const baz: Todo = {
- id: 'abc',
- text: '…',
- done: true
-}`
-
export const jkjo = `// By default, the properties of Todo are
// NOT read-only
type Todo = {
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 1ec2a53..481ff7b 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -589,35 +589,9 @@ const Page = () => (
.
- Finally, how about this one?{' '}
-
- Try pressing .
-
-
- lineIndex === 1}
- compile
- resultError
- result={
- <>{`Type 'string' is not assignable to type 'number'.`}>
- }
- />
-
- This one also failed to compile{' '}
-
- because the id property was string{' '}
- instead of number, as defined on the{' '}
- Todo type:
-
-
- lineIndex === 1}
- />
-
- The bottom line is that TypeScript lets you type check a variable
- against a specified type, which helps you catch mistakes early.
+ The bottom line: TypeScript lets you type check a
+ variable against a specified type, which helps you catch mistakes
+ early.
>
)
@@ -632,8 +606,8 @@ const Page = () => (
<>
Now, let’s use TypeScript to prevent the mistake Little Duckling
- made earlier. To recap, here’s the Todo type we
- created earlier (
+ made. To recap, here’s the Todo type we created
+ earlier (
id is required
@@ -672,7 +646,7 @@ const Page = () => (
/>
Now, let’s copy and paste the code Little Duckling wrote—the one
- without the id property and see what happens.{' '}
+ without the id property—see what happens.{' '}
Press below.
@@ -684,8 +658,8 @@ const Page = () => (
result={`Property 'id' is missing in type '{ text: string; done: boolean; }' but required in type 'Todo'.`}
/>
- It fails with an error because the returned object is missing the{' '}
- id property and therefore does not match the{' '}
+ It failed because the returned object is missing
+ the id property and therefore does not match the{' '}
Todo type. So{' '}
TypeScript can prevent the mistake Little Duckling made!
@@ -714,8 +688,8 @@ const Page = () => (
/>
It compiled! As you can see, TypeScript is great at preventing
- mistakes AND letting you know when everything is implemented
- correctly.
+ mistakes AND letting you know when everything has the correct
+ type.
>
)
From 90fb84cba6a516aa4c24feac6ac699d50d728386 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 22:16:27 +0900
Subject: [PATCH 46/57] Finish slide 7
---
snippets/snippets/todo/longerWidth/wymp.ts | 13 +++++++
snippets/snippets/todo/njgr.ts | 2 +-
snippets/snippets/todo/qbgu.ts | 5 ++-
snippets/snippets/todo/wymp.ts | 16 ---------
src/lib/snippets.ts | 38 +++++++++-----------
src/pages/todo.tsx | 41 +++++++---------------
6 files changed, 46 insertions(+), 69 deletions(-)
create mode 100644 snippets/snippets/todo/longerWidth/wymp.ts
delete mode 100644 snippets/snippets/todo/wymp.ts
diff --git a/snippets/snippets/todo/longerWidth/wymp.ts b/snippets/snippets/todo/longerWidth/wymp.ts
new file mode 100644
index 0000000..140439a
--- /dev/null
+++ b/snippets/snippets/todo/longerWidth/wymp.ts
@@ -0,0 +1,13 @@
+const argument = {
+ id: 1,
+ text: '…',
+ done: true
+}
+
+console.log('Before toggleTodo(), argument is:')
+console.log(argument)
+
+toggleTodo(argument)
+
+console.log('After toggleTodo(), argument is:')
+console.log(argument)
diff --git a/snippets/snippets/todo/njgr.ts b/snippets/snippets/todo/njgr.ts
index 2284593..48d3734 100644
--- a/snippets/snippets/todo/njgr.ts
+++ b/snippets/snippets/todo/njgr.ts
@@ -1,7 +1,7 @@
function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring is a
// bad refactoring because it modifies
- // the original todo object
+ // the argument (input) todo object
todo.done = !todo.done
return todo
}
diff --git a/snippets/snippets/todo/qbgu.ts b/snippets/snippets/todo/qbgu.ts
index f11bff2..0baae9a 100644
--- a/snippets/snippets/todo/qbgu.ts
+++ b/snippets/snippets/todo/qbgu.ts
@@ -1,6 +1,5 @@
-// Returns a new todo object
-// with the opposite boolean value
-// for the "done" proprty.
+// We said earlier that
+// toggleTodo must return a new todo object.
function toggleTodo(todo) {
// ...
}
diff --git a/snippets/snippets/todo/wymp.ts b/snippets/snippets/todo/wymp.ts
deleted file mode 100644
index 184f8c0..0000000
--- a/snippets/snippets/todo/wymp.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-const originalTodo = {
- id: 1,
- text: '…',
- done: true
-}
-
-console.log('Before toggleTodo()…')
-console.log(originalTodo)
-
-const newTodo = toggleTodo(originalTodo)
-
-console.log('After toggleTodo()…')
-console.log('Original Todo:')
-console.log(originalTodo)
-console.log('New Todo:')
-console.log(newTodo)
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 9672a68..7d1efbc 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -655,6 +655,20 @@ function placeToString(place: Place): string {
}
}`
+export const wymp = `const argument = {
+ id: 1,
+ text: '…',
+ done: true
+}
+
+console.log('Before toggleTodo(), argument is:')
+console.log(argument)
+
+toggleTodo(argument)
+
+console.log('After toggleTodo(), argument is:')
+console.log(argument)`
+
export const lund = `const result = toggleTodo({
id: 1,
text: '…',
@@ -691,7 +705,7 @@ const b: Foo = 'hello'`
export const njgr = `function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring is a
// bad refactoring because it modifies
- // the original todo object
+ // the argument (input) todo object
todo.done = !todo.done
return todo
}`
@@ -761,9 +775,8 @@ type ReadonlyFoo = Readonly
// ReadonlyFoo is { readonly bar: number }`
-export const qbgu = `// Returns a new todo object
-// with the opposite boolean value
-// for the "done" proprty.
+export const qbgu = `// We said earlier that
+// toggleTodo must return a new todo object.
function toggleTodo(todo) {
// ...
}`
@@ -899,23 +912,6 @@ export const whae = `function completeAll(
}))
}`
-export const wymp = `const originalTodo = {
- id: 1,
- text: '…',
- done: true
-}
-
-console.log('Before toggleTodo()…')
-console.log(originalTodo)
-
-const newTodo = toggleTodo(originalTodo)
-
-console.log('After toggleTodo()…')
-console.log('Original Todo:')
-console.log(originalTodo)
-console.log('New Todo:')
-console.log(newTodo)`
-
export const xrwn = `type Todo = Readonly<{
// id and text are the same as CompletedTodo
id: number
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 481ff7b..012d93e 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -695,7 +695,7 @@ const Page = () => (
)
},
{
- title: <>Bad refactor>,
+ title: <>Bad refactoring>,
content: (
<>
@@ -737,54 +737,39 @@ const Page = () => (
- Before toggleTodo()…
+ Before toggleTodo(), argument is:
{`{ id: 1, text: '…', done: true }`}
- After toggleTodo()…
-
- Original Todo:
- {`{ id: 1, text: '…', done: false }`}
- New Todo:
+ After toggleTodo(), argument is:
{`{ id: 1, text: '…', done: false }`}
>
}
/>
-
Here’s what happened:
-
-
- originalTodo originally had done: true
- .
-
-
- After toggleTodo(), both originalTodo{' '}
- and newTodo have done: false.
-
-
- So originalTodo was modified!
-
-
- However, we’ve said earlier on this page that{' '}
+ argument changed after running{' '}
+ toggleTodo() on it. This is NOT good because we’ve
+ said earlier that{' '}
- toggleTodo() must return a new todo object. It
- should NOT modify the original object.
-
+ toggleTodo() must return a new todo object.
+
{' '}
+ It should NOT modify the argument (input) todo object.
- That’s why Little Duckling’s refactoring is a bad refactor—even
+ That’s why Little Duckling’s refactoring is a bad refactoring—even
though it compiles correctly.
lineIndex === 2 || lineIndex === 3}
+ shouldHighlight={lineIndex => lineIndex === 4}
/>
Date: Tue, 17 Dec 2019 22:24:09 +0900
Subject: [PATCH 47/57] Wordsmith slide 8
---
snippets/snippets/todo/vgnq.ts | 10 ++++++++--
src/lib/articles.ts | 2 +-
src/lib/snippets.ts | 10 ++++++++--
src/pages/todo.tsx | 12 ++++++------
4 files changed, 23 insertions(+), 11 deletions(-)
diff --git a/snippets/snippets/todo/vgnq.ts b/snippets/snippets/todo/vgnq.ts
index 900ff55..3eb227d 100644
--- a/snippets/snippets/todo/vgnq.ts
+++ b/snippets/snippets/todo/vgnq.ts
@@ -1,5 +1,11 @@
-// This will continue to work because
-// the input todo is not modified
+type Todo = {
+ readonly id: number
+ readonly text: string
+ readonly done: boolean
+}
+
+// Earlier implementation: it will continue to
+// work because the input todo is not modified
function toggleTodo(todo: Todo): Todo {
return {
id: todo.id,
diff --git a/src/lib/articles.ts b/src/lib/articles.ts
index 8e6b1f2..4b914d3 100644
--- a/src/lib/articles.ts
+++ b/src/lib/articles.ts
@@ -8,7 +8,7 @@ export const articlesData = {
todo: {
title:
'TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App',
- date: DateTime.fromISO('2019-12-17T12:00:00Z'),
+ date: DateTime.fromISO('2019-12-18T12:00:00Z'),
description: 'Learn TypeScript by Building a Todo App',
ogImage: 'todo'
},
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 7d1efbc..812b39d 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -865,8 +865,14 @@ export const uxlb = `function toggleTodo(todo: Todo): Todo {
return todo
}`
-export const vgnq = `// This will continue to work because
-// the input todo is not modified
+export const vgnq = `type Todo = {
+ readonly id: number
+ readonly text: string
+ readonly done: boolean
+}
+
+// Earlier implementation: it will continue to
+// work because the input todo is not modified
function toggleTodo(todo: Todo): Todo {
return {
id: todo.id,
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 012d93e..9638ccf 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -807,8 +807,8 @@ const Page = () => (
you can use the readonly keyword in TypeScript.
{' '}
- In the following code, the readonly keyword is added
- to all of the properties of Todo.
+ Here, the readonly keyword is added to all of the
+ properties of Todo.
(
/>
Now, let’s try to compile Little Duckling’s code again using the
- updated definition of Todo. What happens this time?
+ above definition of Todo. What happens this time?
(
}
/>
- By the way, the previous implementation (before refactoring) will
- continue to work because it does NOT modify the input todo item.
+ By the way, the earlier implementation we used will continue to
+ work because it does NOT modify the input todo item.
lineIndex <= 1}
+ shouldHighlight={lineIndex => lineIndex === 6 || lineIndex === 7}
/>
>
)
From bc740c7886f37aa1b4094a85c460baf1d93c3ccf Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 22:41:07 +0900
Subject: [PATCH 48/57] Wordsmith slide 10
---
snippets/snippets/todo/frtm.ts | 2 +-
snippets/snippets/todo/jkjo.ts | 15 -----
src/lib/snippets.ts | 18 +-----
src/pages/todo.tsx | 104 +++++++++++++--------------------
4 files changed, 41 insertions(+), 98 deletions(-)
delete mode 100644 snippets/snippets/todo/jkjo.ts
diff --git a/snippets/snippets/todo/frtm.ts b/snippets/snippets/todo/frtm.ts
index d18fb64..44f7de4 100644
--- a/snippets/snippets/todo/frtm.ts
+++ b/snippets/snippets/todo/frtm.ts
@@ -5,7 +5,7 @@ type Todo = {
}
// Make sure that the input and the output
-// is of the correct type
+// are of the correct type (both must be Todo)
function toggleTodo(todo: Todo): Todo {
// ...
}
diff --git a/snippets/snippets/todo/jkjo.ts b/snippets/snippets/todo/jkjo.ts
deleted file mode 100644
index e45213d..0000000
--- a/snippets/snippets/todo/jkjo.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// By default, the properties of Todo are
-// NOT read-only
-type Todo = {
- id: number
- text: string
- done: boolean
-}
-
-// By using Readonly<> here, it makes the
-// properties readonly only within toggleTodo()
-function toggleTodo(
- todo: Readonly
-): Todo {
- // ...
-}
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 812b39d..26ba19a 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -521,7 +521,7 @@ export const frtm = `type Todo = {
}
// Make sure that the input and the output
-// is of the correct type
+// are of the correct type (both must be Todo)
function toggleTodo(todo: Todo): Todo {
// ...
}`
@@ -544,22 +544,6 @@ export const hszk = `function completeAll(
}))
}`
-export const jkjo = `// By default, the properties of Todo are
-// NOT read-only
-type Todo = {
- id: number
- text: string
- done: boolean
-}
-
-// By using Readonly<> here, it makes the
-// properties readonly only within toggleTodo()
-function toggleTodo(
- todo: Readonly
-): Todo {
- // ...
-}`
-
export const kuzw = `function completeAll(todos: Todo[]): Todo[] {
// We want it to return a new array
// instead of modifying the original array
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 9638ccf..4977cdd 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -880,7 +880,10 @@ const Page = () => (
First, here’s our read-only version of Todo:
-
The above code is equivalent to the following version:
+
+ The above code is equivalent to the following
+ version:
+
@@ -889,60 +892,28 @@ const Page = () => (
}
/>
+ In TypeScript,{' '}
- If you use Readonly<...> on an object type,
- it makes all of its properties readonly
+ if you use the{' '}
+
+ Readonly<...>
+ {' '}
+ keyword on an object type, it makes all of its properties{' '}
+ readonly
- . Here’s another example:
+ . This is often easier than manually adding readonly{' '}
+ to every property.
+
Here’s another example:
- This can be useful for toggleTodo(). For example, we
- might want to:
-
-
-
-
- Make the properties of Todo to be NOT{' '}
- readonly by default, and…
-
-
-
-
- Make them readonly ONLY within{' '}
- toggleTodo().
-
-
-
- That’s so cool!{' '}
-
- So you can convert one type into another type in
- TypeScript?
-
-
- >
- )
- }
- ]}
- />
-
- Yes! In TypeScript, you can use keywords like{' '}
- Readonly<...> to convert one type into another
- type—in this case, it creates a new type with{' '}
+
+ In TypeScript, you can use keywords like{' '}
+ Readonly<...> to convert one type into
+ another type.
+ {' '}
+ In this case, Readonly<...> takes an object
+ type (like Todo) and creates a new object type with{' '}
readonly properties.
(
/>
And the keywords like Readonly<...> are called{' '}
- Mapped Types. There are many built-in mapped
- types (like Required<...>,{' '}
- Partial<...>, etc). You can also create your
- own mapped types. I won’t cover these topics here—you can google
- them.
+ mapped types. Mapped types are kind of like
+ functions, except the input/output are TypeScript types.
+
+
+ There are many built-in mapped types (like{' '}
+ Required<...>, Partial<...>,
+ etc). You can also create your own mapped types. I won’t cover
+ these topics here—you can google them.
- Before TypeScript, you needed to write unit tests to verify these
- things. So in a sense,{' '}
+ In JavaScript, you needed to write unit tests to test these
+ things. But TypeScript can check them automatically. So in a
+ sense,{' '}
- TypeScript’s types act as lightweight unit tests that run
- automatically every time you save (compile) the code.
+ TypeScript’s types act as lightweight unit tests that run 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. You should still write
- tests in TypeScript!)
+ (Of course, this analogy is a simplification. You should still
+ write tests in TypeScript!)
This is especially useful{' '}
@@ -1013,13 +987,13 @@ const Page = () => (
. For example, if you’re using React, you’ll need to transform
data in state updates. You might also need to transform data when
- passing data from a parent component to its child component.
- TypeScript reduces bugs arising from these situations.
+ passing data from a parent component to its children. TypeScript
+ can reduce bugs arising from these situations.
- Some todo apps allow you to{' '}
- mark all items as completed. On the following
- todo app,{' '}
- try pressing “Mark all as completed”:
+ Let’s talk about a new feature of our todo app: “
+ Mark all as completed”.{' '}
+ Try pressing “Mark all as completed” below:
- After pressing “Mark all as completed”, all items will have{' '}
- done: true.
+ After pressing “Mark all as completed”, all
+ items end up with 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.
+ todo items and returns a new array of todos where{' '}
+ done is all true.
Before implementing it,{' '}
- let’s add some types for the input and output of this function
+ let’s specify the input/output types for this function
{' '}
to prevent mistakes!
From 94eb9a6d485f98553af648b70c6fcf918d8a53d0 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Tue, 17 Dec 2019 23:03:23 +0900
Subject: [PATCH 50/57] Slide 13 wordsmith
---
snippets/snippets/todo/lgci.ts | 7 -------
snippets/snippets/todo/mwrj.ts | 2 +-
snippets/snippets/todo/mxqy.ts | 5 +++++
src/lib/snippets.ts | 17 ++++++++---------
src/pages/todo.tsx | 17 +++++++++++------
5 files changed, 25 insertions(+), 23 deletions(-)
create mode 100644 snippets/snippets/todo/mxqy.ts
diff --git a/snippets/snippets/todo/lgci.ts b/snippets/snippets/todo/lgci.ts
index 269eace..e0898ff 100644
--- a/snippets/snippets/todo/lgci.ts
+++ b/snippets/snippets/todo/lgci.ts
@@ -1,10 +1,3 @@
-// Same as before: Each property is readonly
-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/mwrj.ts b/snippets/snippets/todo/mwrj.ts
index 5fbb963..e6d20df 100644
--- a/snippets/snippets/todo/mwrj.ts
+++ b/snippets/snippets/todo/mwrj.ts
@@ -1,4 +1,4 @@
-// After declaring todos as: readonly Todo[],
+// After declaring todos as readonly Todo[],
// the following code WILL NOT compile:
// Compile error - modifies the array
diff --git a/snippets/snippets/todo/mxqy.ts b/snippets/snippets/todo/mxqy.ts
new file mode 100644
index 0000000..6daa488
--- /dev/null
+++ b/snippets/snippets/todo/mxqy.ts
@@ -0,0 +1,5 @@
+type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 26ba19a..f91ef39 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -549,14 +549,7 @@ export const kuzw = `function completeAll(todos: Todo[]): Todo[] {
// instead of modifying the original array
}`
-export const lgci = `// Same as before: Each property is readonly
-type Todo = Readonly<{
- id: number
- text: string
- done: boolean
-}>
-
-// Input is an array of Todo items: Todo[]
+export const lgci = `// Input is an array of Todo items: Todo[]
function completeAll(todos: Todo[]) {
// ...
}`
@@ -669,7 +662,7 @@ function completeAll(todos: Todo[]): Todo[] {
// ...
}`
-export const mwrj = `// After declaring todos as: readonly Todo[],
+export const mwrj = `// After declaring todos as readonly Todo[],
// the following code WILL NOT compile:
// Compile error - modifies the array
@@ -678,6 +671,12 @@ todos[0] = { id: 1, text: '…', done: true }
// Compile error - push() modifies the array
todos.push({ id: 1, text: '…', done: true })`
+export const mxqy = `type Todo = Readonly<{
+ id: number
+ text: string
+ done: boolean
+}>`
+
export const mzyn = `// Creates a union type of number and string
type Foo = number | string
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 711b3ad..051698f 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1078,9 +1078,14 @@ const Page = () => (
content: (
<>
- First, we’ll specify the type for the input
- parameter of completeAll(). It’s an array of{' '}
- Todo items.{' '}
+ For completeAll(), we’ll use the{' '}
+ Readonly version of the Todo type:
+
+
+
+ First, we’ll specify the parameter type of{' '}
+ completeAll(), which is an array of Todo{' '}
+ items.{' '}
To specify an array type, we add [] next to the
type
@@ -1090,7 +1095,7 @@ const Page = () => (
- lineIndex === 8 && tokenIndex >= 5 && tokenIndex <= 9
+ lineIndex === 1 && tokenIndex >= 5 && tokenIndex <= 9
}
/>
@@ -1126,14 +1131,14 @@ const Page = () => (
To make the array itself readonly,
{' '}
- we’ll add the readonly keyword to{' '}
+ we need to add the readonly keyword to{' '}
Todo[] like so:
The new CompletedTodo is almost identical to{' '}
- Todo, except it has done: true instead
- of done: boolean.{' '}
+ Todo, except it has{' '}
+
+ done: true
+ {' '}
+ instead of done: boolean.
+
+
In TypeScript, you can use exact values (like{' '}
true or false) when specifying a type.
@@ -1240,8 +1245,8 @@ const Page = () => (
}
/>
- Let’s take a look at an example. In the following code, we added a{' '}
- CompletedTodo to a todo item that has{' '}
+ Let’s take a look at an example. In the following code, we’ve
+ added CompletedTodo to a todo item that has{' '}
done: false. Let’s see what happens{' '}
when you it
@@ -1253,15 +1258,18 @@ const Page = () => (
compile
resultError
result={<>Type 'false' is not assignable to type 'true'.>}
+ shouldHighlight={(lineIndex, tokenIndex) =>
+ lineIndex === 1 && tokenIndex >= 3 && tokenIndex <= 5
+ }
shouldHighlightResult={lineIndex => lineIndex === 4}
/>
It failed to compile because done is not{' '}
- true. By using literal types like the above, you can
- specify exactly what value is allowed for a property.
+ true. By using literal types, you can specify exactly
+ what value is allowed for a property.
- Coming back to completeAll(), we can now specify the
+ Coming back to completeAll(), we can specify the
return type of completeAll() to be an array of{' '}
CompletedTodo’s:
@@ -1275,7 +1283,7 @@ const Page = () => (
By doing this,{' '}
TypeScript will force you to return an array of todo items where{' '}
- done is true
+ done is all true
—if not, it will result in a compile error.
From 71dc2fc263274545882f90d478d9aea21d97784e Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Wed, 18 Dec 2019 09:12:15 +0900
Subject: [PATCH 52/57] Up to slide 16
---
snippets/snippets/todo/qnwc.ts | 12 ++++++------
src/lib/snippets.ts | 12 ++++++------
src/pages/todo.tsx | 22 ++++++++++++++++++++--
3 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/snippets/snippets/todo/qnwc.ts b/snippets/snippets/todo/qnwc.ts
index dd316e0..e79f458 100644
--- a/snippets/snippets/todo/qnwc.ts
+++ b/snippets/snippets/todo/qnwc.ts
@@ -1,11 +1,11 @@
-// They booth have property x,
-// but B’s x (true) is
-// more specific than A’s x (boolean)
-type A = { x: boolean }
-type B = { x: true }
+// They booth have a property foo,
+// but B’s foo (true) is
+// more specific than A’s foo (boolean)
+type A = { foo: boolean }
+type B = { foo: true }
// This intersection type…
type AandB = A & B
// …is equivalent to:
-type AandB = { x: true }
+type AandB = { foo: true }
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index f91ef39..a47bc07 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -776,17 +776,17 @@ placeToString({ custom: 'Gym' })
placeToString({ custom: 'Supermarket' })
// __supermarket__`
-export const qnwc = `// They booth have property x,
-// but B’s x (true) is
-// more specific than A’s x (boolean)
-type A = { x: boolean }
-type B = { x: true }
+export const qnwc = `// They booth have a property foo,
+// but B’s foo (true) is
+// more specific than A’s foo (boolean)
+type A = { foo: boolean }
+type B = { foo: true }
// This intersection type…
type AandB = A & B
// …is equivalent to:
-type AandB = { x: true }`
+type AandB = { foo: true }`
export const reel = `function toggleTodo(todo) {
return {
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index b7c0d85..ce13301 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1329,8 +1329,11 @@ const Page = () => (
intersection types.
- In TypeScript, you can use the & sign to create
- an intersection type of two types.
+ In TypeScript,{' '}
+
+ you can use the & sign to create an{' '}
+ intersection type of two types.
+
(
done: true. Once again, TypeScript caught an error
early.
+
+ That’s all for this section! By using{' '}
+ completeAll() with a UI library like React, we can
+ build the “Mark all as completed” feature we saw earlier.
+
Let’s add a new feature to our todo app:{' '}
- Place tags.{' '}
+ Place tags.
+
+
- Each todo item can now optionally be tagged with one of the
- following tags:
+ Each todo item can now optionally be tagged with one of
+ the following pre-defined tags:
- Home:
+
- Work:
+
+
+
+
+ Each todo item can also be tagged with a custom,{' '}
+ user-defined tags:
+
+
+
- Custom place:{' '}
,{' '}
, etc—the user
can create any custom place they want.
- People can use this feature to identify which tasks need to be
- done at home, at work, or elsewhere.{' '}
+ Users can use this feature to identify which tasks need to be done
+ at home, at work, or elsewhere.{' '}
It’s optional, so there can be a todo item
without a place tag.
@@ -1596,26 +1605,47 @@ const Page = () => (
Let’s take a look at the associated data.{' '}
- Each todo now can have an optional place property,
- which can have one of the following values:
+ Each todo can now have an optional place property,
+ which determines the place tag:
- So place can be 'home',{' '}
- 'work', or an object containing a string{' '}
- custom property. It can also be missing if there’s no
+ The place property can also be missing if there’s no
place tag.
Here’s the associated data for our previous example:
From 83b09f39bf4f992b8f0c14e4e7c26ab2f8510144 Mon Sep 17 00:00:00 2001
From: Shu Uesugi
Date: Wed, 18 Dec 2019 10:18:55 +0900
Subject: [PATCH 55/57] Wordsmith slide 21
---
snippets/snippets/todo/longerWidth/fawy.ts | 7 ++++++
src/lib/snippets.ts | 8 +++++++
src/pages/todo.tsx | 25 ++++++++++++++++------
3 files changed, 34 insertions(+), 6 deletions(-)
create mode 100644 snippets/snippets/todo/longerWidth/fawy.ts
diff --git a/snippets/snippets/todo/longerWidth/fawy.ts b/snippets/snippets/todo/longerWidth/fawy.ts
new file mode 100644
index 0000000..c4a0d27
--- /dev/null
+++ b/snippets/snippets/todo/longerWidth/fawy.ts
@@ -0,0 +1,7 @@
+type Place = 'home' | 'work' | { custom: string }
+
+// They all compile
+const place1: Place = 'home'
+const place2: Place = 'work'
+const place3: Place = { custom: 'Gym' }
+const place4: Place = { custom: 'Supermarket' }
diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts
index 16b7ef1..28235cf 100644
--- a/src/lib/snippets.ts
+++ b/src/lib/snippets.ts
@@ -578,6 +578,14 @@ function placeToString(place: Place): string {
}
}`
+export const fawy = `type Place = 'home' | 'work' | { custom: string }
+
+// They all compile
+const place1: Place = 'home'
+const place2: Place = 'work'
+const place3: Place = { custom: 'Gym' }
+const place4: Place = { custom: 'Supermarket' }`
+
export const ntup = `// If we have a variable that’s a union type…
type Place = 'home' | 'work' | { custom: string }
diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx
index 2f54846..6d18ced 100644
--- a/src/pages/todo.tsx
+++ b/src/pages/todo.tsx
@@ -1676,8 +1676,11 @@ const Page = () => (
content: (
<>
- To represent place tags, we can use a TypeScript feature called{' '}
- union types. In TypeScript,{' '}
+ To implement place tags, we can use a TypeScript feature called{' '}
+ union types.
+
+
+ In TypeScript,{' '}
you can use the syntax A | B to create a{' '}
union type, which represents a type that’s{' '}
@@ -1730,7 +1733,11 @@ const Page = () => (
}
/>
- Then we can assign the Place type to the{' '}
+ Here’s an example usage of the Place type:
+
+
+
+ We can now assign the Place type to the{' '}
place property of Todo:
(
lineIndex === 4 && tokenIndex >= 3
}
/>
+
+ That’s it! Next, we’ll take a look at{' '}
+ optional properties.
+
>
)
},
@@ -1756,8 +1767,11 @@ const Page = () => (
We briefly mentioned that place tags like{' '}
or are{' '}
optional—we can have todo items without a place
- tag. In our previous example, “Read a book”{' '}
- didn’t have any place tag, so it didn’t have the{' '}
+ tag.
+
+
+ In our previous example, “Read a book”{' '}
+ didn’t have any place tag, so it didn’t have any{' '}
place property:
- We can then use the return value of placeToString(){' '}
- to render place label UIs: ,{' '}
- ,{' '}
- , etc in any UI library
- (e.g. React).
+ We can then use its return value to render place label UIs:{' '}
+ , ,{' '}
+ , etc in any UI library.
+ For example, in React, you can define a functional component and
+ call placeToString() inside it.
- Because place is either 'work' or{' '}
- {`{ custom: string }`} inside else, and{' '}
-
- you can’t do place.custom if place is{' '}
- 'work', TypeScript gives you a compile error.
-
-
+
Here’s what happened:
+
+
+ place is either 'work' or{' '}
+ {`{ custom: string }`} inside else.
+
+
+
+ And place.custom is invalid if place{' '}
+ is 'work'
+
+ .
+
+
- As we just saw,{' '}
+ Summary: As we just saw,{' '}
union types are powerful when combined with conditional
- statements (e.g. if/else or switch)
+ statements (e.g. if/else)
:
@@ -2096,7 +2103,7 @@ const Page = () => (
Finally,{' '}
union types are powerful when combined with conditional
- statements (e.g. if/else or switch).
+ statements (e.g. if/else).
@@ -2120,14 +2127,15 @@ const Page = () => (
- If you’re using React,{' '}
+ Using React? If you’re using React,{' '}
React+TypeScript Cheatsheets
{' '}
is a good reference.
- Once you become more familiar with TypeScript, you should learn{' '}
+ What to learn next: Once you become more
+ familiar with TypeScript, you should learn{' '}
generics next. I’ve written an article on it
called “
@@ -2137,18 +2145,21 @@ const Page = () => (
”.
-
+ Source code:
-
- I plan to write more TypeScript articles by building on the todo
- app example I used here. To get notified when I publish a new
- article, follow me on{' '}
+
{' '}
+
+ I plan to write more TypeScript articles by continuing on the todo
+ app example I used here. To get notified when I publish a new
+ article,{' '}
+
+ follow me on{' '}
Twitter at @chibicode
- .
-