├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .svgrrc.js ├── LICENSE-non-text.txt ├── README.md ├── next-env.d.ts ├── next.config.js ├── package.json ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── fonts │ ├── iosevka-bold.ttf │ ├── iosevka-bold.woff │ ├── iosevka-bold.woff2 │ ├── iosevka-regular.ttf │ ├── iosevka-regular.woff │ └── iosevka-regular.woff2 ├── images │ ├── og-generics.png │ ├── og-index.png │ ├── og-refactor-v3.png │ ├── og-todo-v2.png │ ├── refactor │ │ ├── asOfWriting.png │ │ └── tsdoc.gif │ └── todo │ │ └── backbone-todo.gif ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png ├── safari-pinned-tab.svg ├── site.webmanifest └── sitemap.xml ├── snippets ├── .eslintrc.js ├── bin │ └── generateSnippetsBundle.ts ├── snippets │ ├── generics │ │ ├── bfka.ts │ │ ├── bqvz.ts │ │ ├── brze.ts │ │ ├── bwyu.ts │ │ ├── cbeq.ts │ │ ├── cqrm.ts │ │ ├── cupt.ts │ │ ├── defo.ts │ │ ├── dngl.ts │ │ ├── gjgg.ts │ │ ├── gkgi.ts │ │ ├── gozc.ts │ │ ├── gzwe.ts │ │ ├── hkgv.ts │ │ ├── jdhu.ts │ │ ├── jejx.ts │ │ ├── kbld.ts │ │ ├── lldl.ts │ │ ├── llvc.ts │ │ ├── mngc.ts │ │ ├── mroc.ts │ │ ├── mrub.ts │ │ ├── nbvo.ts │ │ ├── nnyl.ts │ │ ├── nuzz.ts │ │ ├── nyih.ts │ │ ├── osaa.ts │ │ ├── pjcw.ts │ │ ├── qgea.ts │ │ ├── qgxj.ts │ │ ├── qini.ts │ │ ├── qqic.ts │ │ ├── rebo.ts │ │ ├── rxdm.ts │ │ ├── stkh.ts │ │ ├── thxf.ts │ │ ├── udpv.ts │ │ ├── ugeb.ts │ │ ├── wpru.ts │ │ ├── xeax.ts │ │ ├── xekh.ts │ │ ├── xfwf.ts │ │ ├── ystu.ts │ │ ├── zdbq.ts │ │ └── zhql.ts │ ├── refactor │ │ ├── bxzx.ts │ │ ├── crgn.ts │ │ ├── hfdq.ts │ │ ├── ignoreWidth │ │ │ ├── mvsz.ts │ │ │ └── zgvn.ts │ │ ├── lcfe.ts │ │ ├── longerWidth │ │ │ └── riis.ts │ │ ├── lplh.ts │ │ ├── onux.ts │ │ ├── vnfq.ts │ │ └── xwbz.ts │ └── todo │ │ ├── ampt.ts │ │ ├── bnli.ts │ │ ├── bpmz.ts │ │ ├── csum.ts │ │ ├── dqwb.ts │ │ ├── dxfc.ts │ │ ├── eega.ts │ │ ├── frtm.ts │ │ ├── hquv.ts │ │ ├── hszk.ts │ │ ├── kuzw.ts │ │ ├── lgci.ts │ │ ├── lieq.ts │ │ ├── longerWidth │ │ ├── dhor.ts │ │ ├── fawy.ts │ │ ├── ntup.ts │ │ ├── rvyq.ts │ │ ├── szco.ts │ │ ├── umjt.ts │ │ ├── vgja.ts │ │ └── wymp.ts │ │ ├── lund.ts │ │ ├── mnmy.ts │ │ ├── mwrj.ts │ │ ├── mxqy.ts │ │ ├── mzyn.ts │ │ ├── njgr.ts │ │ ├── npah.ts │ │ ├── npgx.ts │ │ ├── npog.ts │ │ ├── ntau.ts │ │ ├── nxyl.ts │ │ ├── okva.ts │ │ ├── oone.ts │ │ ├── qaqa.ts │ │ ├── qbgu.ts │ │ ├── qnrh.ts │ │ ├── qnwc.ts │ │ ├── reel.ts │ │ ├── rlya.ts │ │ ├── rmuo.ts │ │ ├── ruga.ts │ │ ├── szan.ts │ │ ├── tdbp.ts │ │ ├── tgvw.ts │ │ ├── uxlb.ts │ │ ├── vgnq.ts │ │ ├── vpco.ts │ │ ├── wdjp.ts │ │ ├── whae.ts │ │ ├── xrwn.ts │ │ ├── ybhj.ts │ │ ├── yhto.ts │ │ ├── yvpp.ts │ │ ├── ywiv.ts │ │ ├── yxjg.ts │ │ ├── yztr.ts │ │ └── zswn.ts └── tsconfig.json ├── src ├── components │ ├── AboutMe.tsx │ ├── AnimatedSpan.tsx │ ├── ArticleList.tsx │ ├── BubbleQuotes.tsx │ ├── ButtonWithTouchActiveStates.tsx │ ├── Caption.tsx │ ├── Card.tsx │ ├── CardHeadingText.tsx │ ├── CardSubtitleText.tsx │ ├── CardTitleText.tsx │ ├── CodeBlock.tsx │ ├── CodeBlockHighlight.tsx │ ├── CodeBlockPre.tsx │ ├── CodeResult.tsx │ ├── CodeResultWrapper.tsx │ ├── Container.tsx │ ├── ContentTags │ │ ├── A.tsx │ │ ├── Blockquote.tsx │ │ ├── Code.tsx │ │ ├── Highlight.tsx │ │ ├── Hr.tsx │ │ ├── Image.tsx │ │ ├── List.tsx │ │ ├── P.tsx │ │ └── index.tsx │ ├── Emoji │ │ ├── A.tsx │ │ ├── Ampersand.tsx │ │ ├── B.tsx │ │ ├── BadExample.tsx │ │ ├── Bird.tsx │ │ ├── Brain.tsx │ │ ├── Check.tsx │ │ ├── ChickEgg.tsx │ │ ├── CleanCode.tsx │ │ ├── CleanTutorial.tsx │ │ ├── Cross.tsx │ │ ├── CryingCat.tsx │ │ ├── Dash.tsx │ │ ├── Data.tsx │ │ ├── DoneTrue.tsx │ │ ├── DoubleArrow.tsx │ │ ├── FastForward.tsx │ │ ├── Ghost.tsx │ │ ├── HeartCat.tsx │ │ ├── HeartLetter.tsx │ │ ├── Home.tsx │ │ ├── LetterC.tsx │ │ ├── NumberText.tsx │ │ ├── One.tsx │ │ ├── OneTwoThreeFour.tsx │ │ ├── Pin.tsx │ │ ├── Plane.tsx │ │ ├── Prettier.tsx │ │ ├── Question.tsx │ │ ├── Readonly.tsx │ │ ├── RefactorArrow.tsx │ │ ├── Rhino.tsx │ │ ├── Rocket.tsx │ │ ├── Run.tsx │ │ ├── Running.tsx │ │ ├── ScaryCat.tsx │ │ ├── SingleArrow.tsx │ │ ├── Smartphone.tsx │ │ ├── SmilingCat.tsx │ │ ├── Sparkles.tsx │ │ ├── Star.tsx │ │ ├── StringText.tsx │ │ ├── Sweat.tsx │ │ ├── TransformTypechecked.tsx │ │ ├── Twitter.tsx │ │ ├── Type.tsx │ │ ├── UglyCode.tsx │ │ ├── UglyTutorial.tsx │ │ ├── Ui.tsx │ │ ├── UpdatedData.tsx │ │ ├── UpdatedUi.tsx │ │ ├── VerticalBar.tsx │ │ ├── Work.tsx │ │ └── index.tsx │ ├── EmojiSeparator.tsx │ ├── Footer.tsx │ ├── GitHubButton.tsx │ ├── GlobalStyles.tsx │ ├── Header.tsx │ ├── InternalLink.tsx │ ├── Page.tsx │ ├── PlaceLabel.tsx │ ├── PostPage.tsx │ ├── PromptArrowText.tsx │ ├── ReadMore.tsx │ ├── ResultHighlight.tsx │ ├── RunButtonText.tsx │ ├── ThemeContext.tsx │ ├── TodoBlank.tsx │ ├── TodoItem.tsx │ ├── TodoList.tsx │ ├── TodoWithData.tsx │ ├── TodoWithDataContext.ts │ └── TwitterLink.tsx ├── hooks │ └── useTheme.ts ├── lib │ ├── articles.ts │ ├── date.ts │ ├── fixTokens.ts │ ├── meta.ts │ ├── modernNormalize.ts │ ├── prismTheme.ts │ ├── snippets.ts │ ├── theme │ │ ├── colors.ts │ │ ├── fontSizes.ts │ │ ├── letterSpacings.ts │ │ ├── lineHeights.ts │ │ ├── maxWidths.ts │ │ ├── ns.ts │ │ ├── radii.ts │ │ └── spaces.ts │ └── underConstructionCard.tsx └── pages │ ├── _document.tsx │ ├── _error.tsx │ ├── generics.tsx │ ├── index.tsx │ ├── refactor.tsx │ └── todo.tsx ├── tsconfig.json ├── tsconfig.snippets.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": ["emotion"] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | out -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:react/recommended", 6 | "plugin:@typescript-eslint/eslint-recommended", 7 | "plugin:@typescript-eslint/recommended", 8 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 9 | "prettier", 10 | "prettier/@typescript-eslint", 11 | "plugin:prettier/recommended" 12 | ], 13 | "parserOptions": { 14 | "project": "./tsconfig.json" 15 | }, 16 | "plugins": ["@typescript-eslint", "react", "react-hooks"], 17 | "rules": { 18 | "@typescript-eslint/explicit-function-return-type": "off", 19 | "@typescript-eslint/no-explicit-any": "off", 20 | "@typescript-eslint/no-use-before-define": "off", 21 | "@typescript-eslint/no-non-null-assertion": "off", 22 | "@typescript-eslint/no-empty-interface": "off", 23 | "@typescript-eslint/ban-ts-ignore": "off", 24 | "jsx-a11y/anchor-has-content": "off", 25 | "jsx-a11y/anchor-is-valid": "off", 26 | "jsx-a11y/accessible-emoji": "off", 27 | "jsx-a11y/iframe-has-title": "off", 28 | "react-hooks/rules-of-hooks": "error", 29 | "react-hooks/exhaustive-deps": "warn", 30 | "react/no-unescaped-entities": 0 31 | }, 32 | "overrides": [ 33 | { 34 | "files": ["*.js", "*.jsx"], 35 | "rules": { 36 | "@typescript-eslint/no-var-requires": "off" 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | tmp 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v13.0.1 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "overrides": [ 5 | { 6 | "files": "snippets/snippets/**/*.ts", 7 | "options": { 8 | "printWidth": 48 9 | } 10 | }, 11 | { 12 | "files": ["snippets/snippets/**/longerWidth/*.ts"], 13 | "options": { 14 | "printWidth": 53 15 | } 16 | }, 17 | { 18 | "files": ["snippets/snippets/**/ignoreWidth/*.ts"], 19 | "options": { 20 | "printWidth": 120 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.svgrrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | template({ template }, _, { componentName, jsx }) { 3 | const typeScriptTpl = template.smart({ plugins: ['typescript'] }) 4 | return typeScriptTpl.ast` 5 | import React from 'react'; 6 | const ${componentName} = (props: React.SVGProps) => ${jsx}; 7 | export default ${componentName}; 8 | ` 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE-non-text.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 Shusaku Uesugi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [TypeScript for Beginner Programmers](https://ts.chibicode.com) 2 | 3 | This is the repository for the TypeScript tutorial website called **[TypeScript for Beginner Programmers](https://ycombinator.chibicode.com/)**. 4 | 5 |

6 | 7 |

8 | 9 | ## Article 3: [Your Coding Tutorial Might Need Some Refactoring](https://ts.chibicode.com/refactor) 10 | 11 |

12 | 13 |

14 | 15 | ## Article 2: [TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App](https://ts.chibicode.com/todo) 16 | 17 |

18 | 19 |

20 | 21 | ## Article 1: [TypeScript Generics for People Who Gave Up on Understanding Generics](https://ts.chibicode.com/generics) 22 | 23 |

24 | 25 |

26 | 27 | ## License & Credits 28 | 29 | - For emojis, I’m using [Twemoji](https://github.com/twitter/twemoji) by Twitter (CC-BY 4.0 license). 30 | - The text for this website is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). 31 | - Everything else is licensed under the [MIT](LICENSE-non-text.txt) license. 32 | 33 | ## Author 34 | 35 | **Shu Uesugi** 36 | 37 | - [Website](https://chibicode.com) 38 | - Twitter: [@chibicode](https://twitter.com/chibicode) 39 | - Email: [shu.chibicode@gmail.com](mailto:shu.chibicode@gmail.com) -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const config = { 4 | webpack(config) { 5 | config.resolve.alias = Object.assign({}, config.resolve.alias, { 6 | src: path.resolve(__dirname, 'src') 7 | }) 8 | 9 | return config 10 | }, 11 | exportTrailingSlash: true 12 | } 13 | 14 | module.exports = config 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": "14.x" 5 | }, 6 | "dependencies": { 7 | "@emotion/core": "^10.0.22", 8 | "@emotion/styled": "^10.0.23", 9 | "color": "^3.1.2", 10 | "luxon": "^1.21.3", 11 | "next": "^9.3.2", 12 | "prettier": "^1.19.1", 13 | "prism-react-renderer": "^1.0.2", 14 | "react": "^16.12.0", 15 | "react-dom": "^16.12.0", 16 | "react-spring": "^8.0.27" 17 | }, 18 | "scripts": { 19 | "dev": "next", 20 | "build": "next build && next export", 21 | "start": "next start", 22 | "tsc": "tsc", 23 | "ts-node": "ts-node", 24 | "eslint": "eslint --ext .js,.ts,.tsx .", 25 | "eslint:fix": "eslint --ext .js,.ts,.tsx --fix .", 26 | "svgr": "svgr --ext tsx --no-svgo --no-dimensions -d .", 27 | "snippets": "yarn runSnippet snippets/bin/generateSnippetsBundle.ts", 28 | "runSnippet": "ts-node --project tsconfig.snippets.json" 29 | }, 30 | "devDependencies": { 31 | "@svgr/cli": "^4.3.3", 32 | "@types/color": "^3.0.0", 33 | "@types/glob": "^7.1.1", 34 | "@types/luxon": "^1.21.0", 35 | "@types/node": "^12.12.20", 36 | "@types/prettier": "^1.19.0", 37 | "@types/react": "^16.9.16", 38 | "@types/react-dom": "^16.9.4", 39 | "@typescript-eslint/eslint-plugin": "^2.12.0", 40 | "@typescript-eslint/parser": "^2.12.0", 41 | "babel-eslint": "^10.0.3", 42 | "babel-plugin-emotion": "^10.0.23", 43 | "chokidar": "^3.3.1", 44 | "eslint": "^6.7.2", 45 | "eslint-config-prettier": "^6.7.0", 46 | "eslint-plugin-import": "^2.19.1", 47 | "eslint-plugin-prettier": "^3.1.2", 48 | "eslint-plugin-react": "^7.17.0", 49 | "eslint-plugin-react-hooks": "2.3.0", 50 | "glob": "^7.1.6", 51 | "now": "^16.7.0", 52 | "prettier": "^1.19.1", 53 | "ts-node": "^8.5.4", 54 | "typescript": "^3.7.3" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/iosevka-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/fonts/iosevka-bold.ttf -------------------------------------------------------------------------------- /public/fonts/iosevka-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/fonts/iosevka-bold.woff -------------------------------------------------------------------------------- /public/fonts/iosevka-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/fonts/iosevka-bold.woff2 -------------------------------------------------------------------------------- /public/fonts/iosevka-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/fonts/iosevka-regular.ttf -------------------------------------------------------------------------------- /public/fonts/iosevka-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/fonts/iosevka-regular.woff -------------------------------------------------------------------------------- /public/fonts/iosevka-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/fonts/iosevka-regular.woff2 -------------------------------------------------------------------------------- /public/images/og-generics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/images/og-generics.png -------------------------------------------------------------------------------- /public/images/og-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/images/og-index.png -------------------------------------------------------------------------------- /public/images/og-refactor-v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/images/og-refactor-v3.png -------------------------------------------------------------------------------- /public/images/og-todo-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/images/og-todo-v2.png -------------------------------------------------------------------------------- /public/images/refactor/asOfWriting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/images/refactor/asOfWriting.png -------------------------------------------------------------------------------- /public/images/refactor/tsdoc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/images/refactor/tsdoc.gif -------------------------------------------------------------------------------- /public/images/todo/backbone-todo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/images/todo/backbone-todo.gif -------------------------------------------------------------------------------- /public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/mstile-144x144.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/mstile-150x150.png -------------------------------------------------------------------------------- /public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/mstile-310x150.png -------------------------------------------------------------------------------- /public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/mstile-310x310.png -------------------------------------------------------------------------------- /public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chibicode/TypeScript-for-Beginner-Programmers/101e9538196f350a108e97936e9c663364e28cb2/public/mstile-70x70.png -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | https://ts.chibicode.com 9 | 10 | 11 | https://ts.chibicode.com/generics 12 | 13 | -------------------------------------------------------------------------------- /snippets/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const config = require('../.eslintrc') 2 | 3 | module.exports = { 4 | ...config, 5 | parserOptions: { 6 | project: './snippets/tsconfig.json' 7 | }, 8 | root: true 9 | } 10 | -------------------------------------------------------------------------------- /snippets/bin/generateSnippetsBundle.ts: -------------------------------------------------------------------------------- 1 | const glob = require('glob') 2 | const fs = require('fs') 3 | const chokidar = require('chokidar') 4 | 5 | const filePathToKey = (file: string) => 6 | file 7 | .replace(/\.\/snippets\/snippets\/\w+\//, '') 8 | .replace(/longerWidth\//, '') 9 | .replace(/ignoreWidth\//, '') 10 | .replace(/\.ts/, '') 11 | 12 | const regenerate = () => { 13 | glob('./snippets/snippets/**/*.ts', (_: any, files: readonly string[]) => { 14 | if ( 15 | new Set(files.map(filePathToKey)).size !== files.map(filePathToKey).length 16 | ) { 17 | throw new Error('Duplicate file name') 18 | } 19 | 20 | const result = files 21 | .map(file => { 22 | const contents = fs.readFileSync(file, 'utf8') 23 | return `export const ${filePathToKey(file)} = \`${contents 24 | .trim() 25 | .replace(/^;/m, '') 26 | .replace(/`/g, '\\`') 27 | .replace(/\$/g, '\\$')}\`` 28 | }) 29 | .join('\n\n') 30 | 31 | fs.writeFile('./src/lib/snippets.ts', `${result}\n`, (err: any) => { 32 | if (err) { 33 | throw err 34 | } 35 | console.log('snippets generated') 36 | }) 37 | }) 38 | } 39 | 40 | regenerate() 41 | chokidar.watch('./snippets/snippets/**/*.ts').on('change', () => { 42 | regenerate() 43 | }) 44 | -------------------------------------------------------------------------------- /snippets/snippets/generics/bfka.ts: -------------------------------------------------------------------------------- 1 | // We want to modify makeState() to support 2 | // creating two different states: 3 | 4 | // One that only allows numbers, and… 5 | const numState = makeState() 6 | numState.setState(1) 7 | console.log(numState.getState()) // 1 8 | 9 | // The other that only allows strings. 10 | const strState = makeState() 11 | strState.setState('foo') 12 | console.log(strState.getState()) // foo 13 | -------------------------------------------------------------------------------- /snippets/snippets/generics/bqvz.ts: -------------------------------------------------------------------------------- 1 | // Declare a generic function 2 | function genericFunc() { 3 | // You can use T here 4 | } 5 | 6 | // Call it: T will be number 7 | genericFunc() 8 | -------------------------------------------------------------------------------- /snippets/snippets/generics/brze.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | let state: S 3 | 4 | function getState() { 5 | return state 6 | } 7 | 8 | function setState(x: S) { 9 | state = x 10 | } 11 | 12 | return { getState, setState } 13 | } 14 | -------------------------------------------------------------------------------- /snippets/snippets/generics/bwyu.ts: -------------------------------------------------------------------------------- 1 | // Confused by generics code like this? 2 | function makePair< 3 | F extends number | string, 4 | S extends boolean | F 5 | >() 6 | -------------------------------------------------------------------------------- /snippets/snippets/generics/cbeq.ts: -------------------------------------------------------------------------------- 1 | const { getState, setState } = makeState() 2 | 3 | setState(1) 4 | console.log(getState()) 5 | 6 | setState(2) 7 | console.log(getState()) 8 | -------------------------------------------------------------------------------- /snippets/snippets/generics/cqrm.ts: -------------------------------------------------------------------------------- 1 | function makePair() { 2 | // Usage: Pass F for A and S for B 3 | let pair: Pair 4 | 5 | // ... 6 | } 7 | -------------------------------------------------------------------------------- /snippets/snippets/generics/cupt.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | let state: number 3 | 4 | function getState() { 5 | return state 6 | } 7 | 8 | function setState(x: number) { 9 | state = x 10 | } 11 | 12 | return { getState, setState } 13 | } 14 | -------------------------------------------------------------------------------- /snippets/snippets/generics/defo.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | let state: S 3 | 4 | function getState() { 5 | return state 6 | } 7 | 8 | function setState(x: S) { 9 | state = x 10 | } 11 | 12 | return { getState, setState } 13 | } 14 | 15 | const numState = makeState() 16 | numState.setState(1) 17 | console.log(numState.getState()) 18 | 19 | const strState = makeState() 20 | strState.setState('foo') 21 | console.log(strState.getState()) 22 | -------------------------------------------------------------------------------- /snippets/snippets/generics/dngl.ts: -------------------------------------------------------------------------------- 1 | function makeState< 2 | S extends number | string 3 | >() { 4 | let state: S 5 | 6 | function getState() { 7 | return state 8 | } 9 | 10 | function setState(x: S) { 11 | state = x 12 | } 13 | 14 | return { getState, setState } 15 | } 16 | 17 | // What happens if we now pass boolean to S? 18 | const boolState = makeState() 19 | -------------------------------------------------------------------------------- /snippets/snippets/generics/gjgg.ts: -------------------------------------------------------------------------------- 1 | // Creates a number-only state 2 | const numState = makeState() 3 | numState.setState(1) 4 | console.log(numState.getState()) 5 | 6 | // numState.setState('foo') will fail! 7 | -------------------------------------------------------------------------------- /snippets/snippets/generics/gkgi.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | // Change to string 3 | let state: string 4 | 5 | function getState() { 6 | return state 7 | } 8 | 9 | // Accepts a string 10 | function setState(x: string) { 11 | state = x 12 | } 13 | 14 | return { getState, setState } 15 | } 16 | -------------------------------------------------------------------------------- /snippets/snippets/generics/gozc.ts: -------------------------------------------------------------------------------- 1 | // Creates a (number, string) pair 2 | const { getPair, setPair } = makePair< 3 | number, 4 | string 5 | >() 6 | 7 | // Must pass (number, string) 8 | setPair(1, 'hello') 9 | -------------------------------------------------------------------------------- /snippets/snippets/generics/gzwe.ts: -------------------------------------------------------------------------------- 1 | // Don’t need to use 2 | const numState = makeState() 3 | 4 | numState.setState(1) 5 | console.log(numState.getState()) 6 | -------------------------------------------------------------------------------- /snippets/snippets/generics/hkgv.ts: -------------------------------------------------------------------------------- 1 | // Creates a string-only state 2 | const strState = makeState() 3 | strState.setState('foo') 4 | console.log(strState.getState()) 5 | 6 | // strState.setState(1) will fail! 7 | -------------------------------------------------------------------------------- /snippets/snippets/generics/jdhu.ts: -------------------------------------------------------------------------------- 1 | // It sets S as number 2 | makeState() 3 | -------------------------------------------------------------------------------- /snippets/snippets/generics/jejx.ts: -------------------------------------------------------------------------------- 1 | const { getPair, setPair } = makePair() 2 | 3 | setPair(1, 2) 4 | console.log(getPair()) 5 | 6 | setPair(3, 4) 7 | console.log(getPair()) 8 | -------------------------------------------------------------------------------- /snippets/snippets/generics/kbld.ts: -------------------------------------------------------------------------------- 1 | // Limits the type of T 2 | function genericFunc() 3 | 4 | // Success 5 | genericFunc() 6 | // Error 7 | genericFunc() 8 | -------------------------------------------------------------------------------- /snippets/snippets/generics/lldl.ts: -------------------------------------------------------------------------------- 1 | // Extract into a generic interface 2 | // to make it reusable 3 | interface Pair { 4 | first: A 5 | second: B 6 | } 7 | -------------------------------------------------------------------------------- /snippets/snippets/generics/llvc.ts: -------------------------------------------------------------------------------- 1 | // Creates a boolean-only state 2 | const boolState = makeState() 3 | boolState.setState(true) 4 | console.log(boolState.getState()) 5 | -------------------------------------------------------------------------------- /snippets/snippets/generics/mngc.ts: -------------------------------------------------------------------------------- 1 | function makeState() 2 | -------------------------------------------------------------------------------- /snippets/snippets/generics/mroc.ts: -------------------------------------------------------------------------------- 1 | class State { 2 | state: S 3 | 4 | getState() { 5 | return this.state 6 | } 7 | 8 | setState(x: S) { 9 | this.state = x 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /snippets/snippets/generics/mrub.ts: -------------------------------------------------------------------------------- 1 | function makePair() { 2 | let pair: { first: F; second: S } 3 | 4 | // ... 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/generics/nbvo.ts: -------------------------------------------------------------------------------- 1 | function makePair< 2 | F extends number | string = number, 3 | S extends number | string = number 4 | >() 5 | -------------------------------------------------------------------------------- /snippets/snippets/generics/nnyl.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | let state: number 3 | 4 | function getState() { 5 | return state 6 | } 7 | 8 | // setState() expects a number 9 | function setState(x: number) { 10 | state = x 11 | } 12 | 13 | return { getState, setState } 14 | } 15 | -------------------------------------------------------------------------------- /snippets/snippets/generics/nuzz.ts: -------------------------------------------------------------------------------- 1 | function makeState< 2 | A extends number | string, 3 | B extends number | string 4 | >() { 5 | let state: [A, B] 6 | 7 | function getState() { 8 | return state 9 | } 10 | 11 | function setState(first: A, second: B) { 12 | state = [first, second] 13 | } 14 | 15 | return { getState, setState } 16 | } 17 | 18 | const state = makeState() 19 | state.setState(1, 'cat') 20 | console.log(state.getState()) 21 | -------------------------------------------------------------------------------- /snippets/snippets/generics/nyih.ts: -------------------------------------------------------------------------------- 1 | // Set the default type of T 2 | function genericFunc() 3 | 4 | // T will be number inside the function 5 | genericFunc() 6 | -------------------------------------------------------------------------------- /snippets/snippets/generics/osaa.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | // Change to string 3 | let state: string 4 | 5 | function getState() { 6 | return state 7 | } 8 | 9 | // Accepts a string 10 | function setState(x: string) { 11 | state = x 12 | } 13 | 14 | return { getState, setState } 15 | } 16 | 17 | const { getState, setState } = makeState() 18 | 19 | setState('foo') 20 | console.log(getState()) 21 | -------------------------------------------------------------------------------- /snippets/snippets/generics/pjcw.ts: -------------------------------------------------------------------------------- 1 | // Set the default value of x 2 | function regularFunc(x = 2) 3 | 4 | // x will be 2 inside the function 5 | regularFunc() 6 | -------------------------------------------------------------------------------- /snippets/snippets/generics/qgea.ts: -------------------------------------------------------------------------------- 1 | // Extract into a generic type alias. It’s 2 | // basically identical to using an interface 3 | type Pair = { 4 | first: A 5 | second: B 6 | } 7 | -------------------------------------------------------------------------------- /snippets/snippets/generics/qgxj.ts: -------------------------------------------------------------------------------- 1 | // makeState() has 1 type parameter 2 | function makeState() 3 | 4 | // makePair() has 2 type parameters 5 | function makePair() 6 | -------------------------------------------------------------------------------- /snippets/snippets/generics/qini.ts: -------------------------------------------------------------------------------- 1 | // Specify x to be number 2 | function regularFunc(x: number) 3 | 4 | // Success 5 | regularFunc(1) 6 | // Error 7 | regularFunc('foo') 8 | -------------------------------------------------------------------------------- /snippets/snippets/generics/qqic.ts: -------------------------------------------------------------------------------- 1 | // Doesn't work because the created state… 2 | const numAndStrState = makeState() 3 | 4 | // Allows both numbers… 5 | numAndStrState.setState(1) 6 | console.log(numAndStrState.getState()) 7 | 8 | // And strings. 9 | numAndStrState.setState('foo') 10 | console.log(numAndStrState.getState()) 11 | 12 | // This is NOT what we want. We want to create 13 | // a number-only state and a string-only state. 14 | -------------------------------------------------------------------------------- /snippets/snippets/generics/rebo.ts: -------------------------------------------------------------------------------- 1 | // In the function definition of makeState() 2 | let state: S // <- number 3 | 4 | function setState(x: S /* <- number */) { 5 | state = x 6 | } 7 | -------------------------------------------------------------------------------- /snippets/snippets/generics/rxdm.ts: -------------------------------------------------------------------------------- 1 | function makePair() { 2 | let pair: { first: F; second: S } 3 | 4 | function getPair() { 5 | return pair 6 | } 7 | 8 | function setPair(x: F, y: S) { 9 | pair = { 10 | first: x, 11 | second: y 12 | } 13 | } 14 | 15 | return { getPair, setPair } 16 | } 17 | -------------------------------------------------------------------------------- /snippets/snippets/generics/stkh.ts: -------------------------------------------------------------------------------- 1 | const { getState, setState } = makeState() 2 | 3 | // What happens if we use a string instead? 4 | setState('foo') 5 | console.log(getState()) 6 | -------------------------------------------------------------------------------- /snippets/snippets/generics/thxf.ts: -------------------------------------------------------------------------------- 1 | // Set the default type of S as number 2 | function makeState< 3 | S extends number | string = number 4 | >() 5 | -------------------------------------------------------------------------------- /snippets/snippets/generics/udpv.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | let state: number 3 | 4 | function getState() { 5 | return state 6 | } 7 | 8 | function setState(x: number) { 9 | state = x 10 | } 11 | 12 | return { getState, setState } 13 | } 14 | 15 | const { getState, setState } = makeState() 16 | 17 | setState(1) 18 | console.log(getState()) 19 | 20 | setState(2) 21 | console.log(getState()) 22 | -------------------------------------------------------------------------------- /snippets/snippets/generics/ugeb.ts: -------------------------------------------------------------------------------- 1 | function makePair() { 2 | // Stores a pair of values 3 | let pair: { first: number; second: number } 4 | 5 | function getPair() { 6 | return pair 7 | } 8 | 9 | // Stores x as first and y as second 10 | function setPair(x: number, y: number) { 11 | pair = { 12 | first: x, 13 | second: y 14 | } 15 | } 16 | 17 | return { getPair, setPair } 18 | } 19 | -------------------------------------------------------------------------------- /snippets/snippets/generics/wpru.ts: -------------------------------------------------------------------------------- 1 | // Declare a regular function 2 | function regularFunc(x: any) { 3 | // You can use x here 4 | } 5 | 6 | // Call it: x will be 1 7 | regularFunc(1) 8 | -------------------------------------------------------------------------------- /snippets/snippets/generics/xeax.ts: -------------------------------------------------------------------------------- 1 | const { getState, setState } = makeState() 2 | 3 | setState('foo') 4 | console.log(getState()) 5 | -------------------------------------------------------------------------------- /snippets/snippets/generics/xekh.ts: -------------------------------------------------------------------------------- 1 | // The second parameter S must be either 2 | // boolean or whatever was specified for F 3 | function makePair< 4 | F extends number | string, 5 | S extends boolean | F 6 | >() 7 | 8 | // These will work 9 | makePair() 10 | makePair() 11 | makePair() 12 | makePair() 13 | 14 | // This will fail because the second 15 | // parameter must extend boolean | number, 16 | // but instead it’s string 17 | makePair() 18 | -------------------------------------------------------------------------------- /snippets/snippets/generics/xfwf.ts: -------------------------------------------------------------------------------- 1 | // Can we make it so that, is the 2 | // default type paramter of makeState()? 3 | 4 | // We want these two statements to be equivalent 5 | const numState1 = makeState() 6 | const numState2 = makeState() 7 | -------------------------------------------------------------------------------- /snippets/snippets/generics/ystu.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | let state: number | string 3 | 4 | function getState() { 5 | return state 6 | } 7 | 8 | function setState(x: number | string) { 9 | state = x 10 | } 11 | 12 | return { getState, setState } 13 | } 14 | -------------------------------------------------------------------------------- /snippets/snippets/generics/zdbq.ts: -------------------------------------------------------------------------------- 1 | // Pass a type parameter on initialization 2 | const numState = new State() 3 | 4 | numState.setState(1) 5 | 6 | // Prints 1 7 | console.log(numState.getState()) 8 | -------------------------------------------------------------------------------- /snippets/snippets/generics/zhql.ts: -------------------------------------------------------------------------------- 1 | function makeState() { 2 | let state: number 3 | 4 | function getState() { 5 | return state 6 | } 7 | 8 | function setState(x: number) { 9 | state = x 10 | } 11 | 12 | return { getState, setState } 13 | } 14 | 15 | const { getState, setState } = makeState() 16 | 17 | setState('foo') 18 | console.log(getState()) 19 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/bxzx.ts: -------------------------------------------------------------------------------- 1 | // It could have been useful if you could pass 2 | // both number AND string, and have it repeat 3 | // the string the specified number of times 4 | padLeft('Hello world', 4, '#') 5 | // → "####Hello world" 6 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/crgn.ts: -------------------------------------------------------------------------------- 1 | // If the second parameter is string, then 2 | // that string is appended to the left side 3 | padLeft('Hello world', 'Jim: ') 4 | // → "Jim: Hello world" 5 | 6 | // Ask yourself: Would you EVER do this? 7 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/hfdq.ts: -------------------------------------------------------------------------------- 1 | function paddingLeftCss(val: number | string) { 2 | if (typeof val === 'number') { 3 | return `padding-left: ${val * 0.25}rem;` 4 | } else { 5 | return `padding-left: ${val};` 6 | } 7 | } 8 | 9 | // padding-left: 0.25rem; 10 | paddingLeftCss(1) 11 | 12 | // padding-left: 0.5rem; 13 | paddingLeftCss(2) 14 | 15 | // padding-left: 10%; 16 | paddingLeftCss('10%') 17 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/ignoreWidth/mvsz.ts: -------------------------------------------------------------------------------- 1 | function makePair() {} 2 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/ignoreWidth/zgvn.ts: -------------------------------------------------------------------------------- 1 | type Todo = Readonly<{ id: number; text: string; done: boolean; place: Place }> 2 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/lcfe.ts: -------------------------------------------------------------------------------- 1 | // If the second parameter is number, then that 2 | // number of spaces is added to the left side 3 | padLeft('Hello world', 4) 4 | // → " Hello world" 5 | 6 | // If the second parameter is string, then 7 | // that string is appended to the left side 8 | padLeft('Hello world', 'Jim: ') 9 | // → "Jim: Hello world" 10 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/longerWidth/riis.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Takes a string and adds "padding" to the left. 3 | * 4 | * If 'padding' is a number, then that number of 5 | * spaces is added to the left side. 6 | * 7 | * If 'padding' is a string, then 'padding' is 8 | * appended to the left side. 9 | */ 10 | function padLeft( 11 | value: string, 12 | padding: number | string 13 | ) { 14 | if (typeof padding === 'number') { 15 | return Array(padding + 1).join(' ') + value 16 | } else { 17 | return padding + value 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/lplh.ts: -------------------------------------------------------------------------------- 1 | console.log(1 + 2) 2 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/onux.ts: -------------------------------------------------------------------------------- 1 | function extend( 2 | first: First, 3 | second: Second 4 | ): First & Second { 5 | const result: Partial = {} 6 | for (const prop in first) { 7 | if (first.hasOwnProperty(prop)) { 8 | ;(result as First)[prop] = first[prop] 9 | } 10 | } 11 | for (const prop in second) { 12 | if (second.hasOwnProperty(prop)) { 13 | ;(result as Second)[prop] = second[prop] 14 | } 15 | } 16 | return result as First & Second 17 | } 18 | 19 | class Person { 20 | constructor(public name: string) {} 21 | } 22 | 23 | interface Loggable { 24 | log(name: string): void 25 | } 26 | 27 | class ConsoleLogger implements Loggable { 28 | log(name) { 29 | console.log(`Hello, I'm ${name}.`) 30 | } 31 | } 32 | 33 | const jim = extend( 34 | new Person('Jim'), 35 | ConsoleLogger.prototype 36 | ) 37 | jim.log(jim.name) 38 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/vnfq.ts: -------------------------------------------------------------------------------- 1 | type Person = { name: string } 2 | type Loggable = { log: (name: string) => void } 3 | 4 | // Use & to make jim BOTH Person AND Loggable 5 | const jim: Person & Loggable = { 6 | name: 'Jim', 7 | log: name => { 8 | console.log(`Hello, I'm ${name}.`) 9 | } 10 | } 11 | 12 | // "Hello, I’m Jim." 13 | jim.log(jim.name) 14 | -------------------------------------------------------------------------------- /snippets/snippets/refactor/xwbz.ts: -------------------------------------------------------------------------------- 1 | function makePair< 2 | F extends number | string, 3 | S extends boolean | F 4 | >() {} 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/ampt.ts: -------------------------------------------------------------------------------- 1 | function toggleTodo(todo: Todo): Todo { 2 | return { 3 | // This line was missing 4 | id: todo.id, 5 | text: todo.text, 6 | done: !todo.done 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /snippets/snippets/todo/bnli.ts: -------------------------------------------------------------------------------- 1 | const foo: Todo = { 2 | id: 1, 3 | text: '…', 4 | done: true 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/bpmz.ts: -------------------------------------------------------------------------------- 1 | type CompletedTodo = Readonly<{ 2 | id: number 3 | text: string 4 | done: true 5 | }> 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/csum.ts: -------------------------------------------------------------------------------- 1 | // Parameter "todo" must match the Todo type 2 | function toggleTodo(todo: Todo) { 3 | // ... 4 | } 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/dqwb.ts: -------------------------------------------------------------------------------- 1 | function toggleTodo(todo: Todo): Todo { 2 | // Little Duckling’s refactoring 3 | todo.done = !todo.done 4 | return todo 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/dxfc.ts: -------------------------------------------------------------------------------- 1 | // Associated data. If we're using React, this 2 | // would be the component’s props or state 3 | ;[ 4 | { id: 1, text: 'First todo', done: false }, 5 | { id: 2, text: 'Second todo', done: false } 6 | ] 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/eega.ts: -------------------------------------------------------------------------------- 1 | else { 2 | // place = 'work' or { custom: string }, and 3 | // place.custom is invalid if place = 'work' 4 | return 'pinEmoji ' + place.custom 5 | } -------------------------------------------------------------------------------- /snippets/snippets/todo/frtm.ts: -------------------------------------------------------------------------------- 1 | type Todo = { 2 | id: number 3 | text: string 4 | done: boolean 5 | } 6 | 7 | // Make sure that the input and the output 8 | // are of the correct type (both must be Todo) 9 | function toggleTodo(todo: Todo): Todo { 10 | // ... 11 | } 12 | -------------------------------------------------------------------------------- /snippets/snippets/todo/hquv.ts: -------------------------------------------------------------------------------- 1 | ;[ 2 | // ... 3 | // ... 4 | // ... 5 | // ... 6 | // No place property 7 | { id: 5, text: 'Read a book', done: false } 8 | ] 9 | -------------------------------------------------------------------------------- /snippets/snippets/todo/hszk.ts: -------------------------------------------------------------------------------- 1 | function completeAll( 2 | todos: readonly Todo[] 3 | ): CompletedTodo[] { 4 | return todos.map(todo => ({ 5 | ...todo, 6 | done: true 7 | })) 8 | } 9 | -------------------------------------------------------------------------------- /snippets/snippets/todo/kuzw.ts: -------------------------------------------------------------------------------- 1 | function completeAll(todos: Todo[]): Todo[] { 2 | // We want it to return a new array 3 | // instead of modifying the original array 4 | } 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/lgci.ts: -------------------------------------------------------------------------------- 1 | // Input is an array of Todo items: Todo[] 2 | function completeAll(todos: Todo[]) { 3 | // ... 4 | } 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/lieq.ts: -------------------------------------------------------------------------------- 1 | type Todo = { 2 | id: number 3 | text: string 4 | done: boolean 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/dhor.ts: -------------------------------------------------------------------------------- 1 | type Place = 'home' | 'work' | { custom: string } 2 | 3 | // TypeScript knows what the type of "place" 4 | // would be at each point inside the function 5 | function placeToString(place: Place): string { 6 | // In here, place = 'home', 'work' or { custom:… } 7 | 8 | if (place === 'home') { 9 | // In here, place = 'home' 10 | 11 | return 'homeEmoji Home' 12 | } else { 13 | // In here, place = 'work' or { custom: string } 14 | 15 | return 'pinEmoji ' + place.custom 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/fawy.ts: -------------------------------------------------------------------------------- 1 | type Place = 'home' | 'work' | { custom: string } 2 | 3 | // They all compile 4 | const place1: Place = 'home' 5 | const place2: Place = 'work' 6 | const place3: Place = { custom: 'Gym' } 7 | const place4: Place = { custom: 'Supermarket' } 8 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/ntup.ts: -------------------------------------------------------------------------------- 1 | // If we have a variable that’s a union type… 2 | type Place = 'home' | 'work' | { custom: string } 3 | 4 | function placeToString(place: Place): string { 5 | // TypeScript is smart about what the variable’s 6 | // possible values are for each branch of if/else 7 | 8 | if (place === 'home') { 9 | // TypeScript knows place = 'home' here 10 | // (So it won’t compile if you do place.custom) 11 | } else if (place === 'work') { 12 | // TypeScript knows place = 'work' here 13 | // (So it won’t compile if you do place.custom) 14 | } else { 15 | // TypeScript knows place = { custom: … } here 16 | // (So you can do place.custom) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/rvyq.ts: -------------------------------------------------------------------------------- 1 | type Place = 'home' | 'work' | { custom: string } 2 | 3 | type Todo = Readonly<{ 4 | id: number 5 | text: string 6 | done: boolean 7 | // place is optional 8 | place?: Place 9 | }> 10 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/szco.ts: -------------------------------------------------------------------------------- 1 | // Correct implementation 2 | function placeToString(place: Place): string { 3 | if (place === 'home') { 4 | return 'homeEmoji Home' 5 | } else if (place === 'work') { 6 | return 'workEmoji Work' 7 | } else { 8 | // place is guaranteed to be { custom: string } 9 | return 'pinEmoji ' + place.custom 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/umjt.ts: -------------------------------------------------------------------------------- 1 | type Place = 'home' | 'work' | { custom: string } 2 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/vgja.ts: -------------------------------------------------------------------------------- 1 | type Place = 'home' | 'work' | { custom: string } 2 | 3 | // Little Duckling’s implementation 4 | function placeToString(place: Place): string { 5 | if (place === 'home') { 6 | return 'homeEmoji Home' 7 | } else { 8 | return 'pinEmoji ' + place.custom 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /snippets/snippets/todo/longerWidth/wymp.ts: -------------------------------------------------------------------------------- 1 | const argument = { 2 | id: 1, 3 | text: '…', 4 | done: true 5 | } 6 | 7 | console.log('Before toggleTodo(), argument is:') 8 | console.log(argument) 9 | 10 | toggleTodo(argument) 11 | 12 | console.log('After toggleTodo(), argument is:') 13 | console.log(argument) 14 | -------------------------------------------------------------------------------- /snippets/snippets/todo/lund.ts: -------------------------------------------------------------------------------- 1 | const result = toggleTodo({ 2 | id: 1, 3 | text: '…', 4 | done: true 5 | }) 6 | 7 | console.log('Expected:') 8 | console.log(`{ id: 1, text: '…', done: false }`) 9 | console.log('Actual:') 10 | console.log(result) 11 | -------------------------------------------------------------------------------- /snippets/snippets/todo/mnmy.ts: -------------------------------------------------------------------------------- 1 | // Output is an array of Todo items: Todo[] 2 | function completeAll(todos: Todo[]): Todo[] { 3 | // ... 4 | } 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/mwrj.ts: -------------------------------------------------------------------------------- 1 | // After declaring todos as readonly Todo[], 2 | // the following code WILL NOT compile: 3 | 4 | // Compile error - modifies the array 5 | todos[0] = { id: 1, text: '…', done: true } 6 | 7 | // Compile error - push() modifies the array 8 | todos.push({ id: 1, text: '…', done: true }) 9 | -------------------------------------------------------------------------------- /snippets/snippets/todo/mxqy.ts: -------------------------------------------------------------------------------- 1 | type Todo = Readonly<{ 2 | id: number 3 | text: string 4 | done: boolean 5 | }> 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/mzyn.ts: -------------------------------------------------------------------------------- 1 | // Creates a union type of number and string 2 | type Foo = number | string 3 | 4 | // You can assign either a number or a string 5 | // variable to Foo. So these will both compile: 6 | const a: Foo = 1 7 | const b: Foo = 'hello' 8 | -------------------------------------------------------------------------------- /snippets/snippets/todo/njgr.ts: -------------------------------------------------------------------------------- 1 | function toggleTodo(todo: Todo): Todo { 2 | // Little Duckling’s refactoring is a 3 | // bad refactoring because it modifies 4 | // the argument (input) todo object 5 | todo.done = !todo.done 6 | return todo 7 | } 8 | -------------------------------------------------------------------------------- /snippets/snippets/todo/npah.ts: -------------------------------------------------------------------------------- 1 | type Todo = { 2 | readonly id: number 3 | readonly text: string 4 | readonly done: boolean 5 | } 6 | 7 | function toggleTodo(todo: Todo): Todo { 8 | // This won’t compile 9 | todo.done = !todo.done 10 | return todo 11 | } 12 | -------------------------------------------------------------------------------- /snippets/snippets/todo/npgx.ts: -------------------------------------------------------------------------------- 1 | type Todo = Readonly<{ 2 | id: number 3 | text: string 4 | done: boolean 5 | place: Place 6 | }> 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/npog.ts: -------------------------------------------------------------------------------- 1 | type Todo = Readonly<{ 2 | id: number 3 | text: string 4 | done: boolean 5 | // place is optional 6 | place?: Place 7 | }> 8 | -------------------------------------------------------------------------------- /snippets/snippets/todo/ntau.ts: -------------------------------------------------------------------------------- 1 | function toggleTodo(todo: Todo): Todo { 2 | // Little Duckling’s code from earlier: 3 | // Missing the "id" property 4 | return { 5 | text: todo.text, 6 | done: !todo.done 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /snippets/snippets/todo/nxyl.ts: -------------------------------------------------------------------------------- 1 | // Readonly<...> makes each property readonly 2 | type Todo = Readonly<{ 3 | id: number 4 | text: string 5 | done: boolean 6 | }> 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/okva.ts: -------------------------------------------------------------------------------- 1 | console.log( 2 | completeAll([ 3 | { id: 1, text: '…', done: false }, 4 | { id: 2, text: '…', done: true } 5 | ]) 6 | ) 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/oone.ts: -------------------------------------------------------------------------------- 1 | // Returns an array where "done" is all true 2 | function completeAll( 3 | todos: readonly Todo[] 4 | ): CompletedTodo[] { 5 | // ... 6 | } 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/qaqa.ts: -------------------------------------------------------------------------------- 1 | type Foo = { 2 | bar: number 3 | } 4 | 5 | type ReadonlyFoo = Readonly 6 | 7 | // ReadonlyFoo is { readonly bar: number } 8 | -------------------------------------------------------------------------------- /snippets/snippets/todo/qbgu.ts: -------------------------------------------------------------------------------- 1 | // We said earlier that 2 | // toggleTodo must return a new todo object. 3 | function toggleTodo(todo) { 4 | // ... 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/qnrh.ts: -------------------------------------------------------------------------------- 1 | placeToString('home') 2 | // __home__ 3 | 4 | placeToString('work') 5 | // __work__ 6 | 7 | placeToString({ custom: 'Gym' }) 8 | // __gym__ 9 | 10 | placeToString({ custom: 'Supermarket' }) 11 | // __supermarket__ 12 | -------------------------------------------------------------------------------- /snippets/snippets/todo/qnwc.ts: -------------------------------------------------------------------------------- 1 | // They booth have a property foo, 2 | // but B’s foo (true) is 3 | // more specific than A’s foo (boolean) 4 | type A = { foo: boolean } 5 | type B = { foo: true } 6 | 7 | // This intersection type… 8 | type AandB = A & B 9 | 10 | // …is equivalent to: 11 | type AandB = { foo: true } 12 | -------------------------------------------------------------------------------- /snippets/snippets/todo/reel.ts: -------------------------------------------------------------------------------- 1 | function toggleTodo(todo) { 2 | return { 3 | text: todo.text, 4 | done: !todo.done 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/rlya.ts: -------------------------------------------------------------------------------- 1 | type Todo = Readonly<{ 2 | id: number 3 | text: string 4 | done: boolean 5 | }> 6 | 7 | type CompletedTodo = Readonly<{ 8 | id: number 9 | text: string 10 | done: true 11 | }> 12 | -------------------------------------------------------------------------------- /snippets/snippets/todo/rmuo.ts: -------------------------------------------------------------------------------- 1 | type Todo = Readonly<{ 2 | id: number 3 | text: string 4 | done: boolean 5 | }> 6 | 7 | // Override the done property of Todo 8 | type CompletedTodo = Todo & { 9 | readonly done: true 10 | } 11 | -------------------------------------------------------------------------------- /snippets/snippets/todo/ruga.ts: -------------------------------------------------------------------------------- 1 | function completeAll( 2 | todos: readonly Todo[] 3 | ): CompletedTodo[] { 4 | // ... 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/szan.ts: -------------------------------------------------------------------------------- 1 | // Make input todos as readonly array 2 | function completeAll( 3 | todos: readonly Todo[] 4 | ): Todo[] { 5 | // ... 6 | } 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/tdbp.ts: -------------------------------------------------------------------------------- 1 | // Takes an array of todo items and returns 2 | // a new array where "done" is all true 3 | function completeAll(todos) { 4 | // ... 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/tgvw.ts: -------------------------------------------------------------------------------- 1 | const bar: Todo = { 2 | text: '…', 3 | done: true 4 | } 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/uxlb.ts: -------------------------------------------------------------------------------- 1 | function toggleTodo(todo: Todo): Todo { 2 | // Little Duckling’s refactoring 3 | todo.done = !todo.done 4 | return todo 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/vgnq.ts: -------------------------------------------------------------------------------- 1 | type Todo = { 2 | readonly id: number 3 | readonly text: string 4 | readonly done: boolean 5 | } 6 | 7 | // Earlier implementation: it will continue to 8 | // work because the input todo is not modified 9 | function toggleTodo(todo: Todo): Todo { 10 | return { 11 | id: todo.id, 12 | text: todo.text, 13 | done: !todo.done 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /snippets/snippets/todo/vpco.ts: -------------------------------------------------------------------------------- 1 | // Takes a single todo object and returns 2 | // a new todo object containing the opposite 3 | // boolean value for the "done" proprty. 4 | function toggleTodo(todo) { 5 | // ... 6 | } 7 | 8 | // Example usage: 9 | 10 | toggleTodo({ id: …, text: '…', done: true }) 11 | // -> returns { id: …, text: '…', done: false } 12 | 13 | toggleTodo({ id: …, text: '…', done: false }) 14 | // -> returns { id: …, text: '…', done: true } -------------------------------------------------------------------------------- /snippets/snippets/todo/wdjp.ts: -------------------------------------------------------------------------------- 1 | type A = { a: number } 2 | type B = { b: string } 3 | 4 | // This intersection type… 5 | type AandB = A & B 6 | 7 | // …is equivalent to: 8 | type AandB = { 9 | a: number 10 | b: string 11 | } 12 | -------------------------------------------------------------------------------- /snippets/snippets/todo/whae.ts: -------------------------------------------------------------------------------- 1 | function completeAll( 2 | todos: readonly Todo[] 3 | ): CompletedTodo[] { 4 | return todos.map(todo => ({ 5 | ...todo, 6 | // What if we set done to false? 7 | done: false 8 | })) 9 | } 10 | -------------------------------------------------------------------------------- /snippets/snippets/todo/xrwn.ts: -------------------------------------------------------------------------------- 1 | type Todo = Readonly<{ 2 | // id and text are the same as CompletedTodo 3 | id: number 4 | text: string 5 | done: boolean 6 | }> 7 | 8 | type CompletedTodo = Readonly<{ 9 | // id and text are the same as Todo 10 | id: number 11 | text: string 12 | done: true 13 | }> 14 | -------------------------------------------------------------------------------- /snippets/snippets/todo/ybhj.ts: -------------------------------------------------------------------------------- 1 | function placeToString(place: Place): string { 2 | // Takes a Place and returns a string 3 | // that can be used for the place label UI 4 | } 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/yhto.ts: -------------------------------------------------------------------------------- 1 | type Todo = { 2 | readonly id: number 3 | readonly text: string 4 | readonly done: boolean 5 | } 6 | -------------------------------------------------------------------------------- /snippets/snippets/todo/yvpp.ts: -------------------------------------------------------------------------------- 1 | type Foo = { 2 | // bar is an optional property because of "?" 3 | bar?: number 4 | } 5 | 6 | // These will both compile: 7 | // bar can be present or missing 8 | const a: Foo = {} 9 | const b: Foo = { bar: 1 } 10 | -------------------------------------------------------------------------------- /snippets/snippets/todo/ywiv.ts: -------------------------------------------------------------------------------- 1 | // The return value must match the Todo type 2 | function toggleTodo(todo: Todo): Todo { 3 | // ... 4 | } 5 | -------------------------------------------------------------------------------- /snippets/snippets/todo/yxjg.ts: -------------------------------------------------------------------------------- 1 | function toggleTodo(todo) { 2 | return { 3 | // This line was missing 4 | id: todo.id, 5 | text: todo.text, 6 | done: !todo.done 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /snippets/snippets/todo/yztr.ts: -------------------------------------------------------------------------------- 1 | // How to update this to support place labels? 2 | type Todo = Readonly<{ 3 | id: number 4 | text: string 5 | done: boolean 6 | }> 7 | -------------------------------------------------------------------------------- /snippets/snippets/todo/zswn.ts: -------------------------------------------------------------------------------- 1 | // Will this compile? 2 | const testTodo: CompletedTodo = { 3 | id: 1, 4 | text: '…', 5 | done: false 6 | } 7 | -------------------------------------------------------------------------------- /snippets/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.snippets.json" 3 | } 4 | -------------------------------------------------------------------------------- /src/components/AboutMe.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { A, P } from 'src/components/ContentTags' 3 | 4 | const AboutMe = () => ( 5 | <> 6 |

7 | About the author: I’m Shu Uesugi, a software engineer. 8 | The most recent TypeScript project I worked on is an{' '} 9 | interactive computer science course called{' '} 10 | 11 | “Y Combinator for Non-programmers” 12 | 13 | . 14 |

15 |

16 | You can learn more about me on{' '} 17 | my personal website. My email is{' '} 18 | shu.chibicode@gmail.com. 19 |

20 | 21 | ) 22 | 23 | export default AboutMe 24 | -------------------------------------------------------------------------------- /src/components/AnimatedSpan.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSpring, animated } from 'react-spring' 3 | 4 | const AnimatedSpan = ({ 5 | springProps, 6 | children 7 | }: { 8 | springProps: ReturnType[0]> 9 | children: React.ReactNode 10 | }) => { 11 | const props = useSpring(springProps) 12 | return {children} 13 | } 14 | 15 | export default AnimatedSpan 16 | -------------------------------------------------------------------------------- /src/components/ArticleList.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { css, jsx } from '@emotion/core' 3 | import { articleKeys, articlesData } from 'src/lib/articles' 4 | import { dateString } from 'src/lib/date' 5 | import useTheme from 'src/hooks/useTheme' 6 | import InternalLink from 'src/components/InternalLink' 7 | 8 | const ArticleLink = ({ 9 | title, 10 | href, 11 | date 12 | }: { 13 | title: string 14 | href: string 15 | date: string 16 | }) => { 17 | const { 18 | colors, 19 | ns, 20 | fontSizes, 21 | spaces, 22 | lineHeights, 23 | letterSpacings 24 | } = useTheme() 25 | return ( 26 |
  • 32 |

    37 | 55 | {title} 56 | 57 |

    58 |

    65 | {date} 66 |

    67 |
  • 68 | ) 69 | } 70 | 71 | const ArticleList = ({ 72 | ignoreArticleKey 73 | }: { 74 | ignoreArticleKey?: keyof typeof articlesData 75 | }) => { 76 | const { ns, spaces, fontSizes, letterSpacings, colors } = useTheme() 77 | const filteredArticleKeys = articleKeys.filter( 78 | articleKey => articleKey !== ignoreArticleKey 79 | ) 80 | return filteredArticleKeys.length > 0 ? ( 81 | <> 82 |

    96 | {ignoreArticleKey ? 'More ' : ''}Article 97 | {filteredArticleKeys.length > 1 ? 's' : ''} 98 |

    99 |
      106 | {filteredArticleKeys.map(articleKey => ( 107 | 113 | ))} 114 |
    115 | 116 | ) : ( 117 | <> 118 | ) 119 | } 120 | 121 | export default ArticleList 122 | -------------------------------------------------------------------------------- /src/components/BubbleQuotes.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { css, jsx } from '@emotion/core' 3 | import Emoji, { emojiToComponent } from 'src/components/Emoji' 4 | import useTheme from 'src/hooks/useTheme' 5 | import { allColors } from 'src/lib/theme/colors' 6 | 7 | interface BubbleQuoteProps { 8 | type?: keyof typeof emojiToComponent 9 | children: React.ReactNode 10 | backgroundColor?: keyof typeof allColors 11 | } 12 | 13 | type Size = 'md' | 'lg' 14 | 15 | const BubbleQuotes = ({ 16 | quotes, 17 | size = 'md' 18 | }: { 19 | quotes: readonly BubbleQuoteProps[] 20 | size?: Size 21 | }) => { 22 | const { ns, spaces, radii, colors, fontSizes, lineHeights } = useTheme() 23 | return ( 24 |
    34 | {quotes.map(({ type, children, backgroundColor = 'white' }, index) => ( 35 |
    42 | {type && ( 43 |
    56 | 57 |
    58 | )} 59 |
    70 | {children} 71 |
    72 |
    73 | ))} 74 |
    75 | ) 76 | } 77 | 78 | export default BubbleQuotes 79 | -------------------------------------------------------------------------------- /src/components/ButtonWithTouchActiveStates.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { css, jsx } from '@emotion/core' 3 | import { useState } from 'react' 4 | 5 | type ButtonWithTouchActiveStatesProps< 6 | T = JSX.IntrinsicElements['button'] 7 | > = T & { 8 | activeBackgroundColor: string 9 | } 10 | 11 | const ButtonWithTouchActiveStates = ({ 12 | activeBackgroundColor, 13 | ...props 14 | }: ButtonWithTouchActiveStatesProps) => { 15 | const [isActive, setIsActive] = useState(false) 16 | const activate = () => { 17 | if (!props.disabled) { 18 | // NOTE: Originally tried to call the callback here and do e.preventDefault() 19 | // to prevent mouse click event from happening, so that callback fires 20 | // on tap start instead of on tap end, but that was buggy so ended up removing. 21 | setIsActive(true) 22 | } 23 | } 24 | const deactivate = () => { 25 | setIsActive(false) 26 | } 27 | return ( 28 |