From 4b5f8f3873c47073ec31b86e16a921fa25be907e Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Fri, 6 Dec 2019 19:55:53 -0800 Subject: [PATCH] Mapped types --- snippets/snippets/todo/jkjo.ts | 15 ++++ snippets/snippets/todo/nxyl.ts | 6 ++ snippets/snippets/todo/qaqa.ts | 7 ++ src/components/Emoji/Readonly.tsx | 27 +++++++ src/components/Emoji/index.tsx | 4 +- src/lib/snippets.ts | 31 ++++++++ src/pages/todo.tsx | 122 +++++++++++++++++++++++++++++- 7 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 snippets/snippets/todo/jkjo.ts create mode 100644 snippets/snippets/todo/nxyl.ts create mode 100644 snippets/snippets/todo/qaqa.ts create mode 100644 src/components/Emoji/Readonly.tsx diff --git a/snippets/snippets/todo/jkjo.ts b/snippets/snippets/todo/jkjo.ts new file mode 100644 index 0000000..e45213d --- /dev/null +++ b/snippets/snippets/todo/jkjo.ts @@ -0,0 +1,15 @@ +// 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/snippets/snippets/todo/nxyl.ts b/snippets/snippets/todo/nxyl.ts new file mode 100644 index 0000000..d99bb82 --- /dev/null +++ b/snippets/snippets/todo/nxyl.ts @@ -0,0 +1,6 @@ +// Readonly<...> makes each property readonly +type Todo = Readonly<{ + id: number + text: string + done: boolean +}> diff --git a/snippets/snippets/todo/qaqa.ts b/snippets/snippets/todo/qaqa.ts new file mode 100644 index 0000000..d14fa8a --- /dev/null +++ b/snippets/snippets/todo/qaqa.ts @@ -0,0 +1,7 @@ +type Foo = { + bar: number +} + +type ReadonlyFoo = Readonly + +// ReadonlyFoo is { readonly bar: number } diff --git a/src/components/Emoji/Readonly.tsx b/src/components/Emoji/Readonly.tsx new file mode 100644 index 0000000..66cfe4b --- /dev/null +++ b/src/components/Emoji/Readonly.tsx @@ -0,0 +1,27 @@ +import React from 'react' + +const SvgReadonly = (props: React.SVGProps) => ( + + + + + + + + + +) + +export default SvgReadonly diff --git a/src/components/Emoji/index.tsx b/src/components/Emoji/index.tsx index 6b1e25d..48b3c66 100644 --- a/src/components/Emoji/index.tsx +++ b/src/components/Emoji/index.tsx @@ -16,6 +16,7 @@ import UpdatedData from 'src/components/Emoji/UpdatedData' 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' export const emojiToComponent = { bird: Bird, @@ -33,7 +34,8 @@ export const emojiToComponent = { updatedUi: UpdatedUI, updatedData: UpdatedData, sweat: Sweat, - transformTypechecked: TransformTypechecked + transformTypechecked: TransformTypechecked, + readonly: Readonly } export const EmojiWrapper = ({ diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts index 3da8feb..36ac729 100644 --- a/src/lib/snippets.ts +++ b/src/lib/snippets.ts @@ -514,6 +514,22 @@ function toggleTodo(todo: Todo): Todo { // ... }` +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 lieq = `type Todo = { id: number text: string @@ -560,6 +576,21 @@ export const ntau = `function toggleTodo(todo: Todo): Todo { } }` +export const nxyl = `// Readonly<...> makes each property readonly +type Todo = Readonly<{ + id: number + text: string + done: boolean +}>` + +export const qaqa = `type Foo = { + bar: number +} + +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. diff --git a/src/pages/todo.tsx b/src/pages/todo.tsx index 8bfc56e..4286142 100644 --- a/src/pages/todo.tsx +++ b/src/pages/todo.tsx @@ -286,7 +286,7 @@ const Page = () => ( ) } ]} - > + /> ( ) } ]} - > + />

Here’s the correct implementation. It preserves the{' '} id property. @@ -556,7 +556,7 @@ const Page = () => ( ) } ]} - > + />

Try pressing to see if it compiles! @@ -640,7 +640,7 @@ const Page = () => ( ) } ]} - > + />

No worries, Little Duckling! The question is,{' '} @@ -719,6 +719,105 @@ const Page = () => ( ) }, + { + title: ( + <> + The ReadOnly<...> mapped type + + ), + content: ( + <> +

+ In TypeScript,{' '} + + there’s another way to make all properties of an object type + read-only. + {' '} + First, here’s our read-only version of Todo: +

+ +

The above code is equivalent to the following version:

+ + (lineNumber === 1 && tokenNumber >= 7 && tokenNumber <= 8) || + (lineNumber === 5 && tokenNumber >= 1) + } + /> +

+ + If you use Readonly<...> on an object type, + it makes all of its properties readonly + + . 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(). + + +
+

In that case, we can write the following code:

+ + lineNumber === 11 && tokenNumber >= 1 + } + /> + +

+ 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 conovert one type into another + type—in this case, it creates a new type with{' '} + readonly properties. +

+ TypeScript lets you convert one type into another type + } + /> +

+ 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. +

+ + ) + }, { color: 'green', title: <>Types are like lightweight, automatic unit tests, @@ -775,6 +874,21 @@ const Page = () => ( emojis={['data', 'transformTypechecked', 'updatedData']} description={<>TypeScript reduces bugs when transforming data} /> + +

+ Finally, we learned that{' '} + + we can use mapped types like Readonly to convert + one type to another type. + +

+ + (lineNumber === 1 && tokenNumber >= 7 && tokenNumber <= 8) || + (lineNumber === 5 && tokenNumber >= 1) + } + />

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

)