From 2fda910acd5bc5ed84ef2e3697d811bd93da8a54 Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Wed, 20 Nov 2019 20:37:40 -0800 Subject: [PATCH 1/3] Set default type --- snippets/snippets/generics/gzwe.ts | 5 +++ snippets/snippets/generics/nuzz.ts | 26 ++++++++++++ snippets/snippets/generics/thxf.ts | 4 ++ snippets/snippets/generics/xfwf.ts | 6 +++ src/components/ContentTags/Highlight.tsx | 8 +++- src/lib/snippets.ts | 45 ++++++++++++++++++++ src/pages/generics.tsx | 53 ++++++++++++++++++++++-- 7 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 snippets/snippets/generics/gzwe.ts create mode 100644 snippets/snippets/generics/nuzz.ts create mode 100644 snippets/snippets/generics/thxf.ts create mode 100644 snippets/snippets/generics/xfwf.ts diff --git a/snippets/snippets/generics/gzwe.ts b/snippets/snippets/generics/gzwe.ts new file mode 100644 index 0000000..b8e4e5a --- /dev/null +++ b/snippets/snippets/generics/gzwe.ts @@ -0,0 +1,5 @@ +// Don’t need to use +const numState = makeState() + +numState.setState(1) +console.log(numState.getState()) diff --git a/snippets/snippets/generics/nuzz.ts b/snippets/snippets/generics/nuzz.ts new file mode 100644 index 0000000..d55b6a7 --- /dev/null +++ b/snippets/snippets/generics/nuzz.ts @@ -0,0 +1,26 @@ +interface State { + value?: V + author?: A +} + +function makeState< + V extends number | string, + A extends string +>() { + let state: State = {} + + function getState() { + return state + } + + function setState(value: V, author: A) { + state.value = value + state.author = author + } + + return { getState, setState } +} + +const state = makeState() +state.setState(1, 'cat') +console.log(state.getState()) diff --git a/snippets/snippets/generics/thxf.ts b/snippets/snippets/generics/thxf.ts new file mode 100644 index 0000000..24deecd --- /dev/null +++ b/snippets/snippets/generics/thxf.ts @@ -0,0 +1,4 @@ +// Set default type of S as number +function makeState< + S extends number | string = number +>() diff --git a/snippets/snippets/generics/xfwf.ts b/snippets/snippets/generics/xfwf.ts new file mode 100644 index 0000000..538824b --- /dev/null +++ b/snippets/snippets/generics/xfwf.ts @@ -0,0 +1,6 @@ +// Can we make it so that, is the +// default type paramter of makeState()? + +// We want these two statements to be equivalent +const numState1 = makeState() +const numState2 = makeState() diff --git a/src/components/ContentTags/Highlight.tsx b/src/components/ContentTags/Highlight.tsx index e81daef..0dfd599 100644 --- a/src/components/ContentTags/Highlight.tsx +++ b/src/components/ContentTags/Highlight.tsx @@ -1,15 +1,19 @@ /** @jsx jsx */ import { css, jsx } from '@emotion/core' import useTheme from 'src/hooks/useTheme' +import { allColors } from 'src/lib/theme/colors' -const Highlight = (props: JSX.IntrinsicElements['span']) => { +const Highlight = ({ + color, + ...props +}: JSX.IntrinsicElements['span'] & { color?: keyof typeof allColors }) => { const { colors } = useTheme() return ( diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts index 5e10266..e03c158 100644 --- a/src/lib/snippets.ts +++ b/src/lib/snippets.ts @@ -111,6 +111,12 @@ export const gkgi = `function makeState() { return { getState, setState } }` +export const gzwe = `// Don’t need to use +const numState = makeState() + +numState.setState(1) +console.log(numState.getState())` + export const hkgv = `// Creates a string-only state const strState = makeState() strState.setState('foo') @@ -149,6 +155,33 @@ export const nnyl = `function makeState() { return { getState, setState } }` +export const nuzz = `interface State { + value?: V + author?: A +} + +function makeState< + V extends number | string, + A extends string +>() { + let state: State = {} + + function getState() { + return state + } + + function setState(value: V, author: A) { + state.value = value + state.author = author + } + + return { getState, setState } +} + +const state = makeState() +state.setState(1, 'cat') +console.log(state.getState())` + export const osaa = `function makeState() { // Change to string let state: string @@ -197,6 +230,11 @@ export const stkh = `const { getState, setState } = makeState() setState('foo') console.log(getState())` +export const thxf = `// Set default type of S as number +function makeState< + S extends number | string = number +>()` + export const udpv = `function makeState() { let state: number @@ -224,6 +262,13 @@ export const xeax = `const { getState, setState } = makeState() setState('foo') console.log(getState())` +export const xfwf = `// Can we make it so that, is the +// default type paramter of makeState()? + +// We want these two statements to be equivalent +const numState1 = makeState() +const numState2 = makeState()` + export const ystu = `function makeState() { let state: number | string diff --git a/src/pages/generics.tsx b/src/pages/generics.tsx index 4589bc3..8236794 100644 --- a/src/pages/generics.tsx +++ b/src/pages/generics.tsx @@ -51,8 +51,14 @@ const Page = () => ( content: ( <>

- Note: If you didn’t find generics to be - difficult, this tutorial might be too easy for you. + Note: If you already understand generics, you + won’t find anything new in this tutorial. However,{' '} + + you might know someone (maybe your colleague or your Twitter + follower) who’s struggling with generics + + . If so, I’d appreciate it if you could share this article with + them.

) @@ -406,12 +412,51 @@ const Page = () => ( />

It resulted in an error, which is what we want!

- As you just saw, you can specify what’s allowed - for the type argument(s) of a generic function. + As you just saw, you can specify what’s allowed for the type + argument(s) of a generic function.

) }, + { + title: <>Default type, + content: ( + <> +

+ It can be annoying to specify type arguments like{' '} + <number> or <string> every + time you call makeState(). +

+

+ So here’s an idea:{' '} + + Can we make it so that <number> is the + default type argument of makeState()? + {' '} + We want to make it so that, if the type argument is missing, it’s + set as number by default. +

+ +

+ To make this happen, we can specify the default type of{' '} + S by adding = number at the end. It’s + kind of like setting default values for regular function + parameters, right? +

+ + lineNumber === 2 && tokenNumber > 10 && tokenNumber < 14 + } + /> +

+ By doing this, you can create a number-only state without + specifying the type: +

+ + + ) + }, underConstructionCard ]} /> From fd6cf376b9bfebd8690a2ecd68d36ae6467ff8f0 Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Thu, 21 Nov 2019 11:18:19 -0800 Subject: [PATCH 2/3] Up to step 9 --- snippets/snippets/generics/bqvz.ts | 7 ++ snippets/snippets/generics/kbld.ts | 7 ++ snippets/snippets/generics/nuzz.ts | 18 ++--- snippets/snippets/generics/nyih.ts | 5 ++ snippets/snippets/generics/pjcw.ts | 5 ++ snippets/snippets/generics/qini.ts | 7 ++ snippets/snippets/generics/wpru.ts | 7 ++ src/components/Card.tsx | 19 ++--- src/components/ContentTags/Highlight.tsx | 2 +- src/components/ContentTags/Hr.tsx | 22 ++++++ src/components/ContentTags/index.tsx | 1 + src/components/Emoji/Twitter.tsx | 15 ++++ src/components/Emoji/index.tsx | 4 +- src/components/PostPage.tsx | 4 +- src/components/TwitterLink.tsx | 23 ++++++ src/lib/snippets.ts | 64 ++++++++++++---- src/lib/theme/colors.ts | 4 +- src/pages/generics.tsx | 97 ++++++++++++++++++++---- 18 files changed, 255 insertions(+), 56 deletions(-) create mode 100644 snippets/snippets/generics/bqvz.ts create mode 100644 snippets/snippets/generics/kbld.ts create mode 100644 snippets/snippets/generics/nyih.ts create mode 100644 snippets/snippets/generics/pjcw.ts create mode 100644 snippets/snippets/generics/qini.ts create mode 100644 snippets/snippets/generics/wpru.ts create mode 100644 src/components/ContentTags/Hr.tsx create mode 100644 src/components/Emoji/Twitter.tsx create mode 100644 src/components/TwitterLink.tsx diff --git a/snippets/snippets/generics/bqvz.ts b/snippets/snippets/generics/bqvz.ts new file mode 100644 index 0000000..f08611b --- /dev/null +++ b/snippets/snippets/generics/bqvz.ts @@ -0,0 +1,7 @@ +// Declare a generic function +function genericFunc() { + // You can use T here +} + +// Call it: T will be number +genericFunc() diff --git a/snippets/snippets/generics/kbld.ts b/snippets/snippets/generics/kbld.ts new file mode 100644 index 0000000..5cce849 --- /dev/null +++ b/snippets/snippets/generics/kbld.ts @@ -0,0 +1,7 @@ +// Limits the type of T +function genericFunc() + +// Success +genericFunc() +// Error +genericFunc() diff --git a/snippets/snippets/generics/nuzz.ts b/snippets/snippets/generics/nuzz.ts index d55b6a7..d451c03 100644 --- a/snippets/snippets/generics/nuzz.ts +++ b/snippets/snippets/generics/nuzz.ts @@ -1,26 +1,20 @@ -interface State { - value?: V - author?: A -} - function makeState< - V extends number | string, - A extends string + A extends number | string, + B extends number | string >() { - let state: State = {} + let state: [A, B] function getState() { return state } - function setState(value: V, author: A) { - state.value = value - state.author = author + function setState(first: A, second: B) { + state = [first, second] } return { getState, setState } } -const state = makeState() +const state = makeState() state.setState(1, 'cat') console.log(state.getState()) diff --git a/snippets/snippets/generics/nyih.ts b/snippets/snippets/generics/nyih.ts new file mode 100644 index 0000000..ef33b45 --- /dev/null +++ b/snippets/snippets/generics/nyih.ts @@ -0,0 +1,5 @@ +// Set the default type of T +function genericFunc() + +// T will be number inside the function +genericFunc() diff --git a/snippets/snippets/generics/pjcw.ts b/snippets/snippets/generics/pjcw.ts new file mode 100644 index 0000000..7651775 --- /dev/null +++ b/snippets/snippets/generics/pjcw.ts @@ -0,0 +1,5 @@ +// Set default value of x +function regularFunc(x = 2) + +// x will be 2 inside the function +regularFunc() diff --git a/snippets/snippets/generics/qini.ts b/snippets/snippets/generics/qini.ts new file mode 100644 index 0000000..3729689 --- /dev/null +++ b/snippets/snippets/generics/qini.ts @@ -0,0 +1,7 @@ +// Specify x to be number +function regularFunc(x: number) + +// Success +regularFunc(1) +// Error +regularFunc('foo') diff --git a/snippets/snippets/generics/wpru.ts b/snippets/snippets/generics/wpru.ts new file mode 100644 index 0000000..567794e --- /dev/null +++ b/snippets/snippets/generics/wpru.ts @@ -0,0 +1,7 @@ +// Declare a regular function +function regularFunc(x: any) { + // You can use x here +} + +// Call it: x will be 1 +regularFunc(1) diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 121101d..20531ae 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -7,7 +7,7 @@ import { H3 } from 'src/components/ContentTags' export interface CardProps { children: React.ReactNode - color?: 'default' | 'pink' + color?: 'default' | 'pink' | 'green' slideNumber?: number slideCount?: number isLast?: boolean @@ -27,15 +27,8 @@ export const backgroundColor = ( ): keyof typeof allColors => ({ default: 'lightYellow1' as const, - pink: 'lightPink2' as const - }[color]) - -const slideLabelBgColor = ( - color: NonNullable -): keyof typeof allColors => - ({ - default: 'brown' as const, - pink: 'brown' as const + pink: 'lightPink2' as const, + green: 'lightGreen' as const }[color]) const Card = ({ @@ -71,7 +64,7 @@ const Card = ({ font-size: ${fontSizes(0.75)}; line-height: 1; color: ${colors('white')}; - background: ${colors(slideLabelBgColor(color))}; + background: ${colors('brown')}; padding: ${spaces(0.25)} ${spaces(0.5)}; border-radius: 9999px; user-select: none; @@ -80,7 +73,7 @@ const Card = ({ <> Slide{' '} @@ -94,7 +87,7 @@ const Card = ({ {' '} diff --git a/src/components/ContentTags/Highlight.tsx b/src/components/ContentTags/Highlight.tsx index 0dfd599..4e2e9df 100644 --- a/src/components/ContentTags/Highlight.tsx +++ b/src/components/ContentTags/Highlight.tsx @@ -13,7 +13,7 @@ const Highlight = ({ {...props} css={[ css` - background: ${color ? colors(color) : colors('white')}; + background: ${color ? colors(color) : colors('white85')}; ` ]} /> diff --git a/src/components/ContentTags/Hr.tsx b/src/components/ContentTags/Hr.tsx new file mode 100644 index 0000000..d317c8f --- /dev/null +++ b/src/components/ContentTags/Hr.tsx @@ -0,0 +1,22 @@ +/** @jsx jsx */ +import { css, jsx } from '@emotion/core' +import useTheme from 'src/hooks/useTheme' + +const Hr = (props: JSX.IntrinsicElements['hr']) => { + const { colors, spaces } = useTheme() + return ( +
+ ) +} + +export default Hr diff --git a/src/components/ContentTags/index.tsx b/src/components/ContentTags/index.tsx index 8e6b656..1aa7480 100644 --- a/src/components/ContentTags/index.tsx +++ b/src/components/ContentTags/index.tsx @@ -2,4 +2,5 @@ export { default as P } from 'src/components/ContentTags/P' export { default as H3 } from 'src/components/ContentTags/H3' export { default as Code } from 'src/components/ContentTags/Code' export { default as Highlight } from 'src/components/ContentTags/Highlight' +export { default as Hr } from 'src/components/ContentTags/Hr' export { Ul, Ol, UlLi, OlLi } from 'src/components/ContentTags/List' diff --git a/src/components/Emoji/Twitter.tsx b/src/components/Emoji/Twitter.tsx new file mode 100644 index 0000000..ff93634 --- /dev/null +++ b/src/components/Emoji/Twitter.tsx @@ -0,0 +1,15 @@ +import * as React from 'react' + +const Twitter = (props: React.SVGProps) => ( + + + + + +) + +export default Twitter diff --git a/src/components/Emoji/index.tsx b/src/components/Emoji/index.tsx index 6c09a58..cba72f7 100644 --- a/src/components/Emoji/index.tsx +++ b/src/components/Emoji/index.tsx @@ -5,13 +5,15 @@ import CryingCat from 'src/components/Emoji/CryingCat' import Question from 'src/components/Emoji/Question' import Run from 'src/components/Emoji/Run' import ChickEgg from 'src/components/Emoji/ChickEgg' +import Twitter from 'src/components/Emoji/Twitter' export const emojiToComponent = { bird: Bird, cryingCat: CryingCat, question: Question, run: Run, - chickEgg: ChickEgg + chickEgg: ChickEgg, + twitter: Twitter } const Emoji = ({ diff --git a/src/components/PostPage.tsx b/src/components/PostPage.tsx index f057b2b..a146502 100644 --- a/src/components/PostPage.tsx +++ b/src/components/PostPage.tsx @@ -14,6 +14,7 @@ export interface EpisodeCardType { title?: React.ReactNode content?: React.ReactNode footer?: CardProps['footer'] + color?: CardProps['color'] } const PostPage = ({ @@ -96,8 +97,9 @@ const PostPage = ({ } ]} > - {cards.map(({ title, content, footer }, index) => ( + {cards.map(({ title, content, footer, color }, index) => ( { + const tweetUrl = `https://twitter.com/intent/tweet?url=${encodeURIComponent( + url + )}&via=chibicode&text=${encodeURIComponent(title)}` + return ( + + {children} + + ) +} + +export default TwitterLink diff --git a/src/lib/snippets.ts b/src/lib/snippets.ts index e03c158..3eb9dc6 100644 --- a/src/lib/snippets.ts +++ b/src/lib/snippets.ts @@ -11,6 +11,14 @@ const strState = makeState() strState.setState('foo') console.log(strState.getState()) // foo` +export const bqvz = `// Declare a generic function +function genericFunc() { + // You can use T here +} + +// Call it: T will be number +genericFunc()` + export const brze = `function makeState() { let state: S @@ -127,6 +135,14 @@ console.log(strState.getState()) export const jdhu = `// It sets S as number makeState()` +export const kbld = `// Limits the type of T +function genericFunc() + +// Success +genericFunc() +// Error +genericFunc()` + export const kiyi = `// Confused by generics code like this? function getProperty( obj: T, @@ -155,33 +171,33 @@ export const nnyl = `function makeState() { return { getState, setState } }` -export const nuzz = `interface State { - value?: V - author?: A -} - -function makeState< - V extends number | string, - A extends string +export const nuzz = `function makeState< + A extends number | string, + B extends number | string >() { - let state: State = {} + let state: [A, B] function getState() { return state } - function setState(value: V, author: A) { - state.value = value - state.author = author + function setState(first: A, second: B) { + state = [first, second] } return { getState, setState } } -const state = makeState() +const state = makeState() state.setState(1, 'cat') console.log(state.getState())` +export const nyih = `// Set the default type of T +function genericFunc() + +// T will be number inside the function +genericFunc()` + export const osaa = `function makeState() { // Change to string let state: string @@ -203,6 +219,20 @@ const { getState, setState } = makeState() setState('foo') console.log(getState())` +export const pjcw = `// Set default value of x +function regularFunc(x = 2) + +// x will be 2 inside the function +regularFunc()` + +export const qini = `// Specify x to be number +function regularFunc(x: number) + +// Success +regularFunc(1) +// Error +regularFunc('foo')` + export const qqic = `// Doesn't work because the created state… const numAndStrState = makeState() @@ -257,6 +287,14 @@ console.log(getState()) setState(2) console.log(getState())` +export const wpru = `// Declare a regular function +function regularFunc(x: any) { + // You can use x here +} + +// Call it: x will be 1 +regularFunc(1)` + export const xeax = `const { getState, setState } = makeState() setState('foo') diff --git a/src/lib/theme/colors.ts b/src/lib/theme/colors.ts index 9dd545a..8726d59 100644 --- a/src/lib/theme/colors.ts +++ b/src/lib/theme/colors.ts @@ -4,13 +4,13 @@ export const allColors = { lightYellow2: '#FFE8BF', lightPink1: '#FFF2E4', lightPink2: '#FFE7DD', - lightGreen: '#DFE9CE', + lightGreen: '#E4EDD5', brown: '#806538', lightBrown: '#E9D1AC', darkOrange: '#EC9602', pink: '#FFD9CC', white: '#FFFFFF', - white75: 'rgba(255, 255, 255, 0.75)', + white85: 'rgba(255, 255, 255, 0.85)', paleGreen: '#C8DCC7', red: '#DB4003', yellowHighlight: '#FFFF00' diff --git a/src/pages/generics.tsx b/src/pages/generics.tsx index 8236794..6728544 100644 --- a/src/pages/generics.tsx +++ b/src/pages/generics.tsx @@ -1,11 +1,14 @@ import React from 'react' import PostPage from 'src/components/PostPage' -import { P, Code, Highlight, Ul, UlLi } from 'src/components/ContentTags' +import { P, Code, Highlight, Ul, UlLi, Hr } from 'src/components/ContentTags' import EmojiSeparator from 'src/components/EmojiSeparator' import CodeBlock from 'src/components/CodeBlock' import underConstructionCard from 'src/lib/underConstructionCard' import * as snippets from 'src/lib/snippets' import RunButtonText from 'src/components/RunButtonText' +import TwitterLink from 'src/components/TwitterLink' +import { articlesData } from 'src/lib/articles' +import { baseUrl } from 'src/lib/meta' const Page = () => ( (

Note: If you already understand generics, you won’t find anything new in this tutorial. However,{' '} - + you might know someone (maybe your colleague or your Twitter follower) who’s struggling with generics . If so, I’d appreciate it if you could share this article with - them. + them.{' '} + + Click here to tweet this article. +

) @@ -153,7 +162,7 @@ const Page = () => ( />

It failed to compile because setState() expects a - number as its argument. + number:

(

makeState() is now defined as{' '} makeState<S>(). You can think of{' '} - <S> as another argument that you have to pass - in when you call the function. But instead of passing a value, you - pass a type to it. It’s a type argument. + <S> as another thing that you have to pass in + when you call the function. But instead of passing a value, you + pass a type to it.

For example, you can pass the type number as{' '} @@ -368,7 +377,7 @@ const Page = () => ( The solution:{' '} When you declare makeState(), you change the type - argument <S> to{' '} + parameter <S> to{' '} <S extends number | string> . That’s the only change you need to make. @@ -413,7 +422,7 @@ const Page = () => (

It resulted in an error, which is what we want!

As you just saw, you can specify what’s allowed for the type - argument(s) of a generic function. + parameter(s) of a generic function.

) @@ -423,7 +432,7 @@ const Page = () => ( content: ( <>

- It can be annoying to specify type arguments like{' '} + It can be annoying to specify types like{' '} <number> or <string> every time you call makeState().

@@ -431,10 +440,10 @@ const Page = () => ( So here’s an idea:{' '} Can we make it so that <number> is the - default type argument of makeState()? + default type parameter of makeState()? {' '} - We want to make it so that, if the type argument is missing, it’s - set as number by default. + We want to make it so that, if the type is missing, it’s set as{' '} + number by default.

@@ -457,6 +466,68 @@ const Page = () => ( ) }, + { + color: 'green', + title: 'Quick recap: Just like regular function parameters', + content: ( + <> +

+ We are about two-thirds of the way through this article. + Before we continue, let’s do a quick recap. +

+

+ What you should remember is that,{' '} + + generics are just like regular function parameters. + {' '} + The difference is that{' '} + + regular function parameters deal with values, but generics deal + with types. + +

+
+

+ Example 1: For example, here’s a regular function + that takes any value: +

+ +

+ Similarly, you can declare a generic function with a type + parameter: +

+ +
+

+ Example 2: In regular functions, you can specify + the type of a parameter like this: +

+ +

+ Similarly, you can specify what’s allowed for the type parameter + of a generic function: +

+ +
+

+ Example 3: In regular functions, you can specify + the default value of a parameter like this: +

+ +

+ Similarly, you can specify the default type for a generic + function: +

+ +
+

+ Generics are not scary. They’re like regular function parameters, + but instead of values, it deals with types. If you understood this + much, you’re good to go! +

+ + ) + }, underConstructionCard ]} /> From 5a07d340a584a8a4df7e727dec4f48776aca28aa Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Thu, 21 Nov 2019 11:22:51 -0800 Subject: [PATCH 3/3] Modify tweet link --- src/pages/generics.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/generics.tsx b/src/pages/generics.tsx index 6728544..b7e46a4 100644 --- a/src/pages/generics.tsx +++ b/src/pages/generics.tsx @@ -61,12 +61,12 @@ const Page = () => ( follower) who’s struggling with generics . If so, I’d appreciate it if you could share this article with - them.{' '} + them. You can{' '} - Click here to tweet this article. + click here to tweet this article.