From 6171ba2dea760b9e694858fb6f56589545a06f50 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 25 Apr 2023 16:53:11 -0400 Subject: [PATCH 1/4] fix: convert copy-solutions script to .mjs --- .github/workflows/solutions.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/tsc.yml | 2 +- package-lock.json | 257 ++++++++++++++++-- package.json | 1 + .../01-shallow-equality/index.test.ts | 2 +- .../02-shallow-differences/index.test.ts | 2 +- .../03-deep-equality/index.test.ts | 2 +- .../04-deep-differences/index.test.ts | 2 +- .../arrays/text-processor/src/index.test.ts | 2 +- .../01-polly-parrots-properties/index.test.ts | 2 +- .../02-dog-displays/index.test.ts | 2 +- .../03-hamster-helpers/index.test.ts | 2 +- .../classes/horror-factory/src/index.test.ts | 2 +- .../the-shape-of-types/src/index.test.ts | 4 +- .../01-incoming-cipher/index.test.ts | 2 +- .../secret-secrets/02-dr-on/index.test.ts | 2 +- .../03-the-golden-code/index.test.ts | 2 +- .../structural-kitchen/src/index.test.ts | 2 +- .../generics/hidash/01-unique/index.test.ts | 2 +- projects/generics/hidash/02-zip/index.test.ts | 2 +- .../generics/hidash/03-pick/index.test.ts | 2 +- .../generics/hidash/04-pickmany/index.test.ts | 2 +- .../treasure-hunter/src/index.test.ts | 2 +- .../playlist-soundness/src/index.test.ts | 2 +- .../vacation-planning/01-cities/index.test.ts | 2 +- .../02-restaurants/index.test.ts | 2 +- .../03-landmarks/index.test.ts | 2 +- .../01-fabulous-secret-powers/index.test.ts | 2 +- .../02-artifact-assertions/index.test.ts | 2 +- .../type-force/src/index.test.ts | 2 +- .../the-narrow-trail/src/index.test.ts | 2 +- .../temporary-code/src/index.test.ts | 2 +- .../01-favorite-animals/index.test.ts | 2 +- .../02-species-collections/index.test.ts | 2 +- scripts/copy-solutions.mjs | 30 ++ scripts/copy-solutions.sh | 21 -- 37 files changed, 298 insertions(+), 79 deletions(-) create mode 100644 scripts/copy-solutions.mjs delete mode 100755 scripts/copy-solutions.sh diff --git a/.github/workflows/solutions.yml b/.github/workflows/solutions.yml index 098ee0a9..a36e516c 100644 --- a/.github/workflows/solutions.yml +++ b/.github/workflows/solutions.yml @@ -22,7 +22,7 @@ jobs: with: files: projects - name: Copy solution files into index files - run: ./scripts/copy-solutions.sh + run: node ./scripts/copy-solutions.mjs shell: bash - name: Run changed solutions in project directories if: ${{ steps.changed-files.outputs.all_changed_files != '' }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cbd5eca7..32aa778f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: with: files: projects - name: Copy solution files into index files - run: ./scripts/copy-solutions.sh + run: node ./scripts/copy-solutions.mjs shell: bash - name: Run test in project directories if: ${{ steps.changed-files.outputs.all_changed_files != '' }} diff --git a/.github/workflows/tsc.yml b/.github/workflows/tsc.yml index ed48f3e9..24ec698f 100644 --- a/.github/workflows/tsc.yml +++ b/.github/workflows/tsc.yml @@ -22,7 +22,7 @@ jobs: with: files: projects - name: Copy solution files into index files - run: ./scripts/copy-solutions.sh + run: node ./scripts/copy-solutions.mjs shell: bash - name: Run tsc in project directories if: ${{ steps.changed-files.outputs.all_changed_files != '' }} diff --git a/package-lock.json b/package-lock.json index fc191cf1..592deb3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "comment-json": "^4.2.2", "cross-env": "7.0.3", "cspell": "^6.1.2", + "glob": "^10.2.2", "husky": "^8.0.0", "jest": "^29.5.0", "markdownlint-cli": "^0.31.1", @@ -1902,6 +1903,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@sinclair/typebox": { "version": "0.25.24", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", @@ -3338,6 +3349,37 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cspell/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cspell/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/dargs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", @@ -3817,6 +3859,34 @@ "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", + "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -3969,19 +4039,22 @@ } }, "node_modules/glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.2.tgz", + "integrity": "sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.0", + "minipass": "^5.0.0", + "path-scurry": "^1.7.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" }, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4448,6 +4521,24 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.0.tgz", + "integrity": "sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A==", + "dev": true, + "dependencies": { + "cliui": "^7.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", @@ -5859,15 +5950,18 @@ } }, "node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", + "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -5893,6 +5987,15 @@ "node": ">= 6" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -6190,6 +6293,31 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz", + "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==", + "dev": true, + "dependencies": { + "lru-cache": "^9.0.0", + "minipass": "^5.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", + "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -9544,6 +9672,13 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@sinclair/typebox": { "version": "0.25.24", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", @@ -10538,6 +10673,28 @@ "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } } } }, @@ -10979,6 +11136,24 @@ "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", + "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", + "dev": true + } + } + }, "form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -11085,16 +11260,16 @@ } }, "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.2.tgz", + "integrity": "sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.0", + "minipass": "^5.0.0", + "path-scurry": "^1.7.0" } }, "glob-parent": { @@ -11430,6 +11605,16 @@ "istanbul-lib-report": "^3.0.0" } }, + "jackspeak": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.0.tgz", + "integrity": "sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A==", + "dev": true, + "requires": { + "@pkgjs/parseargs": "^0.11.0", + "cliui": "^7.0.4" + } + }, "jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", @@ -12511,9 +12696,9 @@ "dev": true }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", + "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -12536,6 +12721,12 @@ "kind-of": "^6.0.3" } }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true + }, "mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -12751,6 +12942,24 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-scurry": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz", + "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==", + "dev": true, + "requires": { + "lru-cache": "^9.0.0", + "minipass": "^5.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", + "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "dev": true + } + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", diff --git a/package.json b/package.json index b13c782a..5709dd99 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "comment-json": "^4.2.2", "cross-env": "7.0.3", "cspell": "^6.1.2", + "glob": "^10.2.2", "husky": "^8.0.0", "jest": "^29.5.0", "markdownlint-cli": "^0.31.1", diff --git a/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts b/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts index ca84712d..8135db42 100644 --- a/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts +++ b/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { shallowEquality } = process.env.TEST_SOLUTIONS ? solution : index; +const { shallowEquality } = process.env.TEST_SOLUTIONS ? index : index; describe(shallowEquality, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts b/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts index cbe76ac6..d93ee019 100644 --- a/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts +++ b/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { shallowDifferences } = process.env.TEST_SOLUTIONS ? solution : index; +const { shallowDifferences } = process.env.TEST_SOLUTIONS ? index : index; describe(shallowDifferences, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts b/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts index b675c522..e143020f 100644 --- a/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts +++ b/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { deepEquality } = process.env.TEST_SOLUTIONS ? solution : index; +const { deepEquality } = process.env.TEST_SOLUTIONS ? index : index; describe(deepEquality, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts b/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts index 321f8850..a071a2ab 100644 --- a/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts +++ b/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { deepDifferences } = process.env.TEST_SOLUTIONS ? solution : index; +const { deepDifferences } = process.env.TEST_SOLUTIONS ? index : index; describe(deepDifferences, () => { test.each([ diff --git a/projects/arrays/text-processor/src/index.test.ts b/projects/arrays/text-processor/src/index.test.ts index 397332a5..deead27a 100644 --- a/projects/arrays/text-processor/src/index.test.ts +++ b/projects/arrays/text-processor/src/index.test.ts @@ -6,7 +6,7 @@ import * as solution from "./solution"; process.env.TEST_SOLUTIONS = "1"; -const { alignTexts } = process.env.TEST_SOLUTIONS ? solution : index; +const { alignTexts } = process.env.TEST_SOLUTIONS ? index : index; describe(alignTexts, () => { describe("types", () => { diff --git a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts index 7ed1e02a..5907de60 100644 --- a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts +++ b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Parrot } = process.env.TEST_SOLUTIONS ? solution : index; +const { Parrot } = process.env.TEST_SOLUTIONS ? index : index; const mockRandom = jest.spyOn(Math, "random"); diff --git a/projects/classes/classifying-creatures/02-dog-displays/index.test.ts b/projects/classes/classifying-creatures/02-dog-displays/index.test.ts index 68175edc..3479e9af 100644 --- a/projects/classes/classifying-creatures/02-dog-displays/index.test.ts +++ b/projects/classes/classifying-creatures/02-dog-displays/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Puppy } = process.env.TEST_SOLUTIONS ? solution : index; +const { Puppy } = process.env.TEST_SOLUTIONS ? index : index; describe(Puppy, () => { describe("properties", () => { diff --git a/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts b/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts index aa8ac806..4907d1e0 100644 --- a/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts +++ b/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Gerbil, Hamster } = process.env.TEST_SOLUTIONS ? solution : index; +const { Gerbil, Hamster } = process.env.TEST_SOLUTIONS ? index : index; describe(Gerbil, () => { describe("species", () => { diff --git a/projects/classes/horror-factory/src/index.test.ts b/projects/classes/horror-factory/src/index.test.ts index 14005c39..84c2e7d2 100644 --- a/projects/classes/horror-factory/src/index.test.ts +++ b/projects/classes/horror-factory/src/index.test.ts @@ -4,7 +4,7 @@ import * as index from "./index"; import * as solution from "./solution"; const { createDemon, createSorcerer, Horror } = process.env.TEST_SOLUTIONS - ? solution + ? index : index; const createMockHorrorSettings = (evil: boolean) => { diff --git a/projects/classes/the-shape-of-types/src/index.test.ts b/projects/classes/the-shape-of-types/src/index.test.ts index 9569aea4..19e3b543 100644 --- a/projects/classes/the-shape-of-types/src/index.test.ts +++ b/projects/classes/the-shape-of-types/src/index.test.ts @@ -4,11 +4,11 @@ import * as index from "./index"; import * as solution from "./solution"; const { Demon, Horror, Sorcerer } = process.env.TEST_SOLUTIONS - ? solution + ? index : // In theory, it would be nice to not have to apply this cast // In practice, TypeScript's structural typing does not play well with # privates // See https://github.com/LearningTypeScript/projects/issues/183 - (index as unknown as typeof solution); + (index as unknown as typeof index); class MockHorror extends Horror { name = ""; diff --git a/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts b/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts index 6549f1d7..f2b56a16 100644 --- a/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts +++ b/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createCipher } = process.env.TEST_SOLUTIONS ? solution : index; +const { createCipher } = process.env.TEST_SOLUTIONS ? index : index; describe(createCipher, () => { describe("types", () => { diff --git a/projects/functions/secret-secrets/02-dr-on/index.test.ts b/projects/functions/secret-secrets/02-dr-on/index.test.ts index 5b52a16d..85636c1c 100644 --- a/projects/functions/secret-secrets/02-dr-on/index.test.ts +++ b/projects/functions/secret-secrets/02-dr-on/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createAdvancedCipher } = process.env.TEST_SOLUTIONS ? solution : index; +const { createAdvancedCipher } = process.env.TEST_SOLUTIONS ? index : index; describe(createAdvancedCipher, () => { describe("types", () => { diff --git a/projects/functions/secret-secrets/03-the-golden-code/index.test.ts b/projects/functions/secret-secrets/03-the-golden-code/index.test.ts index e47bc634..fd8a6368 100644 --- a/projects/functions/secret-secrets/03-the-golden-code/index.test.ts +++ b/projects/functions/secret-secrets/03-the-golden-code/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createCodeCracker } = process.env.TEST_SOLUTIONS ? solution : index; +const { createCodeCracker } = process.env.TEST_SOLUTIONS ? index : index; describe(createCodeCracker, () => { describe("types", () => { diff --git a/projects/functions/structural-kitchen/src/index.test.ts b/projects/functions/structural-kitchen/src/index.test.ts index 82329961..5096e1b9 100644 --- a/projects/functions/structural-kitchen/src/index.test.ts +++ b/projects/functions/structural-kitchen/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, jest } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { createKitchen } = process.env.TEST_SOLUTIONS ? solution : index; +const { createKitchen } = process.env.TEST_SOLUTIONS ? index : index; describe(createKitchen, () => { describe("announce", () => { diff --git a/projects/generics/hidash/01-unique/index.test.ts b/projects/generics/hidash/01-unique/index.test.ts index afd55461..c148e207 100644 --- a/projects/generics/hidash/01-unique/index.test.ts +++ b/projects/generics/hidash/01-unique/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { unique } = process.env.TEST_SOLUTIONS ? solution : index; +const { unique } = process.env.TEST_SOLUTIONS ? index : index; describe(unique, () => { test.each([ diff --git a/projects/generics/hidash/02-zip/index.test.ts b/projects/generics/hidash/02-zip/index.test.ts index 5ed475da..b715e028 100644 --- a/projects/generics/hidash/02-zip/index.test.ts +++ b/projects/generics/hidash/02-zip/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { zip } = process.env.TEST_SOLUTIONS ? solution : index; +const { zip } = process.env.TEST_SOLUTIONS ? index : index; describe(zip, () => { test("types", () => { diff --git a/projects/generics/hidash/03-pick/index.test.ts b/projects/generics/hidash/03-pick/index.test.ts index 1e49a2fd..58cea558 100644 --- a/projects/generics/hidash/03-pick/index.test.ts +++ b/projects/generics/hidash/03-pick/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { pick } = process.env.TEST_SOLUTIONS ? solution : index; +const { pick } = process.env.TEST_SOLUTIONS ? index : index; describe(pick, () => { test("types", () => { diff --git a/projects/generics/hidash/04-pickmany/index.test.ts b/projects/generics/hidash/04-pickmany/index.test.ts index f6e8dfb5..e04f6356 100644 --- a/projects/generics/hidash/04-pickmany/index.test.ts +++ b/projects/generics/hidash/04-pickmany/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { pickMany } = process.env.TEST_SOLUTIONS ? solution : index; +const { pickMany } = process.env.TEST_SOLUTIONS ? index : index; describe(pickMany, () => { describe("types", () => { diff --git a/projects/generics/treasure-hunter/src/index.test.ts b/projects/generics/treasure-hunter/src/index.test.ts index 5029b14c..8a603054 100644 --- a/projects/generics/treasure-hunter/src/index.test.ts +++ b/projects/generics/treasure-hunter/src/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { collectTreasure } = process.env.TEST_SOLUTIONS ? solution : index; +const { collectTreasure } = process.env.TEST_SOLUTIONS ? index : index; describe(collectTreasure, () => { describe("types", () => { diff --git a/projects/interfaces/playlist-soundness/src/index.test.ts b/projects/interfaces/playlist-soundness/src/index.test.ts index 441a88a3..91e79dd1 100644 --- a/projects/interfaces/playlist-soundness/src/index.test.ts +++ b/projects/interfaces/playlist-soundness/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { unrollPlaylist } = process.env.TEST_SOLUTIONS ? solution : index; +const { unrollPlaylist } = process.env.TEST_SOLUTIONS ? index : index; describe(unrollPlaylist, () => { test.each<[solution.PlaylistItem[], unknown]>([ diff --git a/projects/interfaces/vacation-planning/01-cities/index.test.ts b/projects/interfaces/vacation-planning/01-cities/index.test.ts index fedfc577..50be8d22 100644 --- a/projects/interfaces/vacation-planning/01-cities/index.test.ts +++ b/projects/interfaces/vacation-planning/01-cities/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { describeCity } = process.env.TEST_SOLUTIONS ? solution : index; +const { describeCity } = process.env.TEST_SOLUTIONS ? index : index; const dedent = (text: TemplateStringsArray) => text[0].replaceAll(/\n\s*/g, "\n").trim(); diff --git a/projects/interfaces/vacation-planning/02-restaurants/index.test.ts b/projects/interfaces/vacation-planning/02-restaurants/index.test.ts index 4fa84222..034dac4b 100644 --- a/projects/interfaces/vacation-planning/02-restaurants/index.test.ts +++ b/projects/interfaces/vacation-planning/02-restaurants/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { groupRestaurants } = process.env.TEST_SOLUTIONS ? solution : index; +const { groupRestaurants } = process.env.TEST_SOLUTIONS ? index : index; describe(groupRestaurants, () => { test.each([ diff --git a/projects/interfaces/vacation-planning/03-landmarks/index.test.ts b/projects/interfaces/vacation-planning/03-landmarks/index.test.ts index 30420f41..1fc608ec 100644 --- a/projects/interfaces/vacation-planning/03-landmarks/index.test.ts +++ b/projects/interfaces/vacation-planning/03-landmarks/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { describeLandmark } = process.env.TEST_SOLUTIONS ? solution : index; +const { describeLandmark } = process.env.TEST_SOLUTIONS ? index : index; const dedent = (text: TemplateStringsArray) => text[0].replaceAll(/\n\s*/g, "\n").trim(); diff --git a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts index 95e0eab3..69d0aa2e 100644 --- a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts +++ b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { announceCharacter } = process.env.TEST_SOLUTIONS ? solution : index; +const { announceCharacter } = process.env.TEST_SOLUTIONS ? index : index; const mockLog = (console.log = jest.fn()); diff --git a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts index 80acb065..3f4b6608 100644 --- a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts +++ b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { getArtifactType } = process.env.TEST_SOLUTIONS ? solution : index; +const { getArtifactType } = process.env.TEST_SOLUTIONS ? index : index; describe(getArtifactType, () => { describe("type", () => { diff --git a/projects/type-modifiers/type-force/src/index.test.ts b/projects/type-modifiers/type-force/src/index.test.ts index 7267cd67..b2bab081 100644 --- a/projects/type-modifiers/type-force/src/index.test.ts +++ b/projects/type-modifiers/type-force/src/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { duel } = process.env.TEST_SOLUTIONS ? solution : index; +const { duel } = process.env.TEST_SOLUTIONS ? index : index; describe(duel, () => { describe("types", () => { diff --git a/projects/unions-and-literals/the-narrow-trail/src/index.test.ts b/projects/unions-and-literals/the-narrow-trail/src/index.test.ts index 59265c22..a9b6c07e 100644 --- a/projects/unions-and-literals/the-narrow-trail/src/index.test.ts +++ b/projects/unions-and-literals/the-narrow-trail/src/index.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, jest, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { runCommands } = process.env.TEST_SOLUTIONS ? solution : index; +const { runCommands } = process.env.TEST_SOLUTIONS ? index : index; const mockRandom = jest.spyOn(Math, "random"); diff --git a/projects/using-ide-features/temporary-code/src/index.test.ts b/projects/using-ide-features/temporary-code/src/index.test.ts index 0c8e4297..c809dc44 100644 --- a/projects/using-ide-features/temporary-code/src/index.test.ts +++ b/projects/using-ide-features/temporary-code/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./index.solution"; -const { announceExamplePeople } = process.env.TEST_SOLUTIONS ? solution : index; +const { announceExamplePeople } = process.env.TEST_SOLUTIONS ? index : index; describe(announceExamplePeople, () => { it("does not include its own list of persons", () => { diff --git a/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts b/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts index b6710490..3de27e2e 100644 --- a/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts +++ b/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts @@ -4,7 +4,7 @@ import * as index from "./index"; import * as solution from "./solution"; const { checkIsAnyAnimalFavorite, getFavoriteAnimals, logFavoriteAnimals } = - process.env.TEST_SOLUTIONS ? solution : index; + process.env.TEST_SOLUTIONS ? index : index; describe(checkIsAnyAnimalFavorite, () => { it("returns true for a favorite animal", () => { diff --git a/projects/using-ide-features/typearium/02-species-collections/index.test.ts b/projects/using-ide-features/typearium/02-species-collections/index.test.ts index 59c14cce..0537cc6d 100644 --- a/projects/using-ide-features/typearium/02-species-collections/index.test.ts +++ b/projects/using-ide-features/typearium/02-species-collections/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./index.solution"; -const { getEverything } = process.env.TEST_SOLUTIONS ? solution : index; +const { getEverything } = process.env.TEST_SOLUTIONS ? index : index; describe(getEverything, () => { it("returns nothing with no settings", () => { diff --git a/scripts/copy-solutions.mjs b/scripts/copy-solutions.mjs new file mode 100644 index 00000000..033b8549 --- /dev/null +++ b/scripts/copy-solutions.mjs @@ -0,0 +1,30 @@ +import chalk from "chalk"; +import fs from "fs/promises"; +import { glob } from "glob"; + +console.log(chalk.blue("Starting"), chalk.cyanBright("copy-solutions.mjs")); + +for (const testFile of await glob("projects/**/*.test.*")) { + console.log(chalk.gray("Replacing solution with index in", testFile)); + await fs.writeFile( + testFile, + (await fs.readFile(testFile)) + .toString() + .replaceAll("? solution", "? index") + .replaceAll(" as typeof solution", " as typeof index ") + ); +} + +for (const solutionPath of await glob("projects/**/*solution*")) { + const replacementPath = solutionPath.includes("solution.") + ? solutionPath.replace("solution", "index") + : solutionPath.replace(".solution", ""); + + if (solutionPath === replacementPath) { + console.log( + chalk.gray("Skipping", solutionPath, "(no replacement found).") + ); + } else { + console.log(chalk.gray("Copying", solutionPath, "to", replacementPath)); + } +} diff --git a/scripts/copy-solutions.sh b/scripts/copy-solutions.sh deleted file mode 100755 index a73c6cc6..00000000 --- a/scripts/copy-solutions.sh +++ /dev/null @@ -1,21 +0,0 @@ -# Replacing "? solution" with "? index" means test files only import from the index. -# Projects that use classes with # private members would otherwise report assignability errors. -find projects -type f -name "*.test.*" -print0 | xargs -0 sed -i '' -e 's/\? solution/\? index/g' - -# Similarly, "index as unknown as typeof solution" type assertions are used for class #privates. -find projects -type f -name "*.test.*" -print0 | xargs -0 sed -i '' -e 's/as typeof solution/as typeof index/g' - -for solutionPath in $(find projects -name \*solution\*); do - if [[ $solutionPath == */solution.* ]]; then - replacedPath=${solutionPath//"solution"/"index"} - else - replacedPath=${solutionPath//".solution"/""} - fi - - if [[ "$solutionPath" == "$replacedPath" ]]; then - echo "Skipping \"$solutionPath\" (no replacement found)." - else - echo "Copying from \"$solutionPath\" to \"$replacedPath\"." - cp $solutionPath $replacedPath - fi -done From fddeb9f5732fa6a5681ac824a1b247056b039bad Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 25 Apr 2023 16:55:54 -0400 Subject: [PATCH 2/4] Undo accidental changes --- .../arrays/analyzing-dna/01-shallow-equality/index.test.ts | 2 +- .../arrays/analyzing-dna/02-shallow-differences/index.test.ts | 2 +- projects/arrays/analyzing-dna/03-deep-equality/index.test.ts | 2 +- .../arrays/analyzing-dna/04-deep-differences/index.test.ts | 2 +- projects/arrays/text-processor/src/index.test.ts | 2 +- .../01-polly-parrots-properties/index.test.ts | 2 +- .../classifying-creatures/02-dog-displays/index.test.ts | 2 +- .../classifying-creatures/03-hamster-helpers/index.test.ts | 2 +- projects/classes/horror-factory/src/index.test.ts | 2 +- projects/classes/the-shape-of-types/src/index.test.ts | 4 ++-- .../functions/secret-secrets/01-incoming-cipher/index.test.ts | 2 +- projects/functions/secret-secrets/02-dr-on/index.test.ts | 2 +- .../functions/secret-secrets/03-the-golden-code/index.test.ts | 2 +- projects/functions/structural-kitchen/src/index.test.ts | 2 +- projects/generics/hidash/01-unique/index.test.ts | 2 +- projects/generics/hidash/02-zip/index.test.ts | 2 +- projects/generics/hidash/03-pick/index.test.ts | 2 +- projects/generics/hidash/04-pickmany/index.test.ts | 2 +- projects/generics/treasure-hunter/src/index.test.ts | 2 +- projects/interfaces/playlist-soundness/src/index.test.ts | 2 +- projects/interfaces/vacation-planning/01-cities/index.test.ts | 2 +- .../interfaces/vacation-planning/02-restaurants/index.test.ts | 2 +- .../interfaces/vacation-planning/03-landmarks/index.test.ts | 2 +- .../01-fabulous-secret-powers/index.test.ts | 2 +- .../02-artifact-assertions/index.test.ts | 2 +- projects/type-modifiers/type-force/src/index.test.ts | 2 +- .../unions-and-literals/the-narrow-trail/src/index.test.ts | 2 +- projects/using-ide-features/temporary-code/src/index.test.ts | 2 +- .../typearium/01-favorite-animals/index.test.ts | 2 +- .../typearium/02-species-collections/index.test.ts | 2 +- 30 files changed, 31 insertions(+), 31 deletions(-) diff --git a/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts b/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts index 8135db42..ca84712d 100644 --- a/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts +++ b/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { shallowEquality } = process.env.TEST_SOLUTIONS ? index : index; +const { shallowEquality } = process.env.TEST_SOLUTIONS ? solution : index; describe(shallowEquality, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts b/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts index d93ee019..cbe76ac6 100644 --- a/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts +++ b/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { shallowDifferences } = process.env.TEST_SOLUTIONS ? index : index; +const { shallowDifferences } = process.env.TEST_SOLUTIONS ? solution : index; describe(shallowDifferences, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts b/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts index e143020f..b675c522 100644 --- a/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts +++ b/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { deepEquality } = process.env.TEST_SOLUTIONS ? index : index; +const { deepEquality } = process.env.TEST_SOLUTIONS ? solution : index; describe(deepEquality, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts b/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts index a071a2ab..321f8850 100644 --- a/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts +++ b/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { deepDifferences } = process.env.TEST_SOLUTIONS ? index : index; +const { deepDifferences } = process.env.TEST_SOLUTIONS ? solution : index; describe(deepDifferences, () => { test.each([ diff --git a/projects/arrays/text-processor/src/index.test.ts b/projects/arrays/text-processor/src/index.test.ts index deead27a..397332a5 100644 --- a/projects/arrays/text-processor/src/index.test.ts +++ b/projects/arrays/text-processor/src/index.test.ts @@ -6,7 +6,7 @@ import * as solution from "./solution"; process.env.TEST_SOLUTIONS = "1"; -const { alignTexts } = process.env.TEST_SOLUTIONS ? index : index; +const { alignTexts } = process.env.TEST_SOLUTIONS ? solution : index; describe(alignTexts, () => { describe("types", () => { diff --git a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts index 5907de60..7ed1e02a 100644 --- a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts +++ b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Parrot } = process.env.TEST_SOLUTIONS ? index : index; +const { Parrot } = process.env.TEST_SOLUTIONS ? solution : index; const mockRandom = jest.spyOn(Math, "random"); diff --git a/projects/classes/classifying-creatures/02-dog-displays/index.test.ts b/projects/classes/classifying-creatures/02-dog-displays/index.test.ts index 3479e9af..68175edc 100644 --- a/projects/classes/classifying-creatures/02-dog-displays/index.test.ts +++ b/projects/classes/classifying-creatures/02-dog-displays/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Puppy } = process.env.TEST_SOLUTIONS ? index : index; +const { Puppy } = process.env.TEST_SOLUTIONS ? solution : index; describe(Puppy, () => { describe("properties", () => { diff --git a/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts b/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts index 4907d1e0..aa8ac806 100644 --- a/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts +++ b/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Gerbil, Hamster } = process.env.TEST_SOLUTIONS ? index : index; +const { Gerbil, Hamster } = process.env.TEST_SOLUTIONS ? solution : index; describe(Gerbil, () => { describe("species", () => { diff --git a/projects/classes/horror-factory/src/index.test.ts b/projects/classes/horror-factory/src/index.test.ts index 84c2e7d2..14005c39 100644 --- a/projects/classes/horror-factory/src/index.test.ts +++ b/projects/classes/horror-factory/src/index.test.ts @@ -4,7 +4,7 @@ import * as index from "./index"; import * as solution from "./solution"; const { createDemon, createSorcerer, Horror } = process.env.TEST_SOLUTIONS - ? index + ? solution : index; const createMockHorrorSettings = (evil: boolean) => { diff --git a/projects/classes/the-shape-of-types/src/index.test.ts b/projects/classes/the-shape-of-types/src/index.test.ts index 19e3b543..9569aea4 100644 --- a/projects/classes/the-shape-of-types/src/index.test.ts +++ b/projects/classes/the-shape-of-types/src/index.test.ts @@ -4,11 +4,11 @@ import * as index from "./index"; import * as solution from "./solution"; const { Demon, Horror, Sorcerer } = process.env.TEST_SOLUTIONS - ? index + ? solution : // In theory, it would be nice to not have to apply this cast // In practice, TypeScript's structural typing does not play well with # privates // See https://github.com/LearningTypeScript/projects/issues/183 - (index as unknown as typeof index); + (index as unknown as typeof solution); class MockHorror extends Horror { name = ""; diff --git a/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts b/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts index f2b56a16..6549f1d7 100644 --- a/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts +++ b/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createCipher } = process.env.TEST_SOLUTIONS ? index : index; +const { createCipher } = process.env.TEST_SOLUTIONS ? solution : index; describe(createCipher, () => { describe("types", () => { diff --git a/projects/functions/secret-secrets/02-dr-on/index.test.ts b/projects/functions/secret-secrets/02-dr-on/index.test.ts index 85636c1c..5b52a16d 100644 --- a/projects/functions/secret-secrets/02-dr-on/index.test.ts +++ b/projects/functions/secret-secrets/02-dr-on/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createAdvancedCipher } = process.env.TEST_SOLUTIONS ? index : index; +const { createAdvancedCipher } = process.env.TEST_SOLUTIONS ? solution : index; describe(createAdvancedCipher, () => { describe("types", () => { diff --git a/projects/functions/secret-secrets/03-the-golden-code/index.test.ts b/projects/functions/secret-secrets/03-the-golden-code/index.test.ts index fd8a6368..e47bc634 100644 --- a/projects/functions/secret-secrets/03-the-golden-code/index.test.ts +++ b/projects/functions/secret-secrets/03-the-golden-code/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createCodeCracker } = process.env.TEST_SOLUTIONS ? index : index; +const { createCodeCracker } = process.env.TEST_SOLUTIONS ? solution : index; describe(createCodeCracker, () => { describe("types", () => { diff --git a/projects/functions/structural-kitchen/src/index.test.ts b/projects/functions/structural-kitchen/src/index.test.ts index 5096e1b9..82329961 100644 --- a/projects/functions/structural-kitchen/src/index.test.ts +++ b/projects/functions/structural-kitchen/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, jest } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { createKitchen } = process.env.TEST_SOLUTIONS ? index : index; +const { createKitchen } = process.env.TEST_SOLUTIONS ? solution : index; describe(createKitchen, () => { describe("announce", () => { diff --git a/projects/generics/hidash/01-unique/index.test.ts b/projects/generics/hidash/01-unique/index.test.ts index c148e207..afd55461 100644 --- a/projects/generics/hidash/01-unique/index.test.ts +++ b/projects/generics/hidash/01-unique/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { unique } = process.env.TEST_SOLUTIONS ? index : index; +const { unique } = process.env.TEST_SOLUTIONS ? solution : index; describe(unique, () => { test.each([ diff --git a/projects/generics/hidash/02-zip/index.test.ts b/projects/generics/hidash/02-zip/index.test.ts index b715e028..5ed475da 100644 --- a/projects/generics/hidash/02-zip/index.test.ts +++ b/projects/generics/hidash/02-zip/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { zip } = process.env.TEST_SOLUTIONS ? index : index; +const { zip } = process.env.TEST_SOLUTIONS ? solution : index; describe(zip, () => { test("types", () => { diff --git a/projects/generics/hidash/03-pick/index.test.ts b/projects/generics/hidash/03-pick/index.test.ts index 58cea558..1e49a2fd 100644 --- a/projects/generics/hidash/03-pick/index.test.ts +++ b/projects/generics/hidash/03-pick/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { pick } = process.env.TEST_SOLUTIONS ? index : index; +const { pick } = process.env.TEST_SOLUTIONS ? solution : index; describe(pick, () => { test("types", () => { diff --git a/projects/generics/hidash/04-pickmany/index.test.ts b/projects/generics/hidash/04-pickmany/index.test.ts index e04f6356..f6e8dfb5 100644 --- a/projects/generics/hidash/04-pickmany/index.test.ts +++ b/projects/generics/hidash/04-pickmany/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { pickMany } = process.env.TEST_SOLUTIONS ? index : index; +const { pickMany } = process.env.TEST_SOLUTIONS ? solution : index; describe(pickMany, () => { describe("types", () => { diff --git a/projects/generics/treasure-hunter/src/index.test.ts b/projects/generics/treasure-hunter/src/index.test.ts index 8a603054..5029b14c 100644 --- a/projects/generics/treasure-hunter/src/index.test.ts +++ b/projects/generics/treasure-hunter/src/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { collectTreasure } = process.env.TEST_SOLUTIONS ? index : index; +const { collectTreasure } = process.env.TEST_SOLUTIONS ? solution : index; describe(collectTreasure, () => { describe("types", () => { diff --git a/projects/interfaces/playlist-soundness/src/index.test.ts b/projects/interfaces/playlist-soundness/src/index.test.ts index 91e79dd1..441a88a3 100644 --- a/projects/interfaces/playlist-soundness/src/index.test.ts +++ b/projects/interfaces/playlist-soundness/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { unrollPlaylist } = process.env.TEST_SOLUTIONS ? index : index; +const { unrollPlaylist } = process.env.TEST_SOLUTIONS ? solution : index; describe(unrollPlaylist, () => { test.each<[solution.PlaylistItem[], unknown]>([ diff --git a/projects/interfaces/vacation-planning/01-cities/index.test.ts b/projects/interfaces/vacation-planning/01-cities/index.test.ts index 50be8d22..fedfc577 100644 --- a/projects/interfaces/vacation-planning/01-cities/index.test.ts +++ b/projects/interfaces/vacation-planning/01-cities/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { describeCity } = process.env.TEST_SOLUTIONS ? index : index; +const { describeCity } = process.env.TEST_SOLUTIONS ? solution : index; const dedent = (text: TemplateStringsArray) => text[0].replaceAll(/\n\s*/g, "\n").trim(); diff --git a/projects/interfaces/vacation-planning/02-restaurants/index.test.ts b/projects/interfaces/vacation-planning/02-restaurants/index.test.ts index 034dac4b..4fa84222 100644 --- a/projects/interfaces/vacation-planning/02-restaurants/index.test.ts +++ b/projects/interfaces/vacation-planning/02-restaurants/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { groupRestaurants } = process.env.TEST_SOLUTIONS ? index : index; +const { groupRestaurants } = process.env.TEST_SOLUTIONS ? solution : index; describe(groupRestaurants, () => { test.each([ diff --git a/projects/interfaces/vacation-planning/03-landmarks/index.test.ts b/projects/interfaces/vacation-planning/03-landmarks/index.test.ts index 1fc608ec..30420f41 100644 --- a/projects/interfaces/vacation-planning/03-landmarks/index.test.ts +++ b/projects/interfaces/vacation-planning/03-landmarks/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { describeLandmark } = process.env.TEST_SOLUTIONS ? index : index; +const { describeLandmark } = process.env.TEST_SOLUTIONS ? solution : index; const dedent = (text: TemplateStringsArray) => text[0].replaceAll(/\n\s*/g, "\n").trim(); diff --git a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts index 69d0aa2e..95e0eab3 100644 --- a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts +++ b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { announceCharacter } = process.env.TEST_SOLUTIONS ? index : index; +const { announceCharacter } = process.env.TEST_SOLUTIONS ? solution : index; const mockLog = (console.log = jest.fn()); diff --git a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts index 3f4b6608..80acb065 100644 --- a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts +++ b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { getArtifactType } = process.env.TEST_SOLUTIONS ? index : index; +const { getArtifactType } = process.env.TEST_SOLUTIONS ? solution : index; describe(getArtifactType, () => { describe("type", () => { diff --git a/projects/type-modifiers/type-force/src/index.test.ts b/projects/type-modifiers/type-force/src/index.test.ts index b2bab081..7267cd67 100644 --- a/projects/type-modifiers/type-force/src/index.test.ts +++ b/projects/type-modifiers/type-force/src/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { duel } = process.env.TEST_SOLUTIONS ? index : index; +const { duel } = process.env.TEST_SOLUTIONS ? solution : index; describe(duel, () => { describe("types", () => { diff --git a/projects/unions-and-literals/the-narrow-trail/src/index.test.ts b/projects/unions-and-literals/the-narrow-trail/src/index.test.ts index a9b6c07e..59265c22 100644 --- a/projects/unions-and-literals/the-narrow-trail/src/index.test.ts +++ b/projects/unions-and-literals/the-narrow-trail/src/index.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, jest, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { runCommands } = process.env.TEST_SOLUTIONS ? index : index; +const { runCommands } = process.env.TEST_SOLUTIONS ? solution : index; const mockRandom = jest.spyOn(Math, "random"); diff --git a/projects/using-ide-features/temporary-code/src/index.test.ts b/projects/using-ide-features/temporary-code/src/index.test.ts index c809dc44..0c8e4297 100644 --- a/projects/using-ide-features/temporary-code/src/index.test.ts +++ b/projects/using-ide-features/temporary-code/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./index.solution"; -const { announceExamplePeople } = process.env.TEST_SOLUTIONS ? index : index; +const { announceExamplePeople } = process.env.TEST_SOLUTIONS ? solution : index; describe(announceExamplePeople, () => { it("does not include its own list of persons", () => { diff --git a/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts b/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts index 3de27e2e..b6710490 100644 --- a/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts +++ b/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts @@ -4,7 +4,7 @@ import * as index from "./index"; import * as solution from "./solution"; const { checkIsAnyAnimalFavorite, getFavoriteAnimals, logFavoriteAnimals } = - process.env.TEST_SOLUTIONS ? index : index; + process.env.TEST_SOLUTIONS ? solution : index; describe(checkIsAnyAnimalFavorite, () => { it("returns true for a favorite animal", () => { diff --git a/projects/using-ide-features/typearium/02-species-collections/index.test.ts b/projects/using-ide-features/typearium/02-species-collections/index.test.ts index 0537cc6d..59c14cce 100644 --- a/projects/using-ide-features/typearium/02-species-collections/index.test.ts +++ b/projects/using-ide-features/typearium/02-species-collections/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./index.solution"; -const { getEverything } = process.env.TEST_SOLUTIONS ? index : index; +const { getEverything } = process.env.TEST_SOLUTIONS ? solution : index; describe(getEverything, () => { it("returns nothing with no settings", () => { From ec11a88d01975285a9fa8624bc43697fa7fcade2 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 25 Apr 2023 17:00:05 -0400 Subject: [PATCH 3/4] Helps to actually replace the file --- scripts/copy-solutions.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/copy-solutions.mjs b/scripts/copy-solutions.mjs index 033b8549..a396ac6f 100644 --- a/scripts/copy-solutions.mjs +++ b/scripts/copy-solutions.mjs @@ -16,15 +16,17 @@ for (const testFile of await glob("projects/**/*.test.*")) { } for (const solutionPath of await glob("projects/**/*solution*")) { - const replacementPath = solutionPath.includes("solution.") + const replacedPath = solutionPath.includes("solution.") ? solutionPath.replace("solution", "index") : solutionPath.replace(".solution", ""); - if (solutionPath === replacementPath) { + if (solutionPath === replacedPath) { console.log( chalk.gray("Skipping", solutionPath, "(no replacement found).") ); } else { - console.log(chalk.gray("Copying", solutionPath, "to", replacementPath)); + console.log(chalk.gray("Copying", solutionPath, "to", replacedPath)); } + + await fs.copyFile(solutionPath, replacedPath); } From 88e13bba593bcbc401802e18051866410821a770 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 25 Apr 2023 17:05:20 -0400 Subject: [PATCH 4/4] Correct solution script search --- .../01-shallow-equality/index.test.ts | 2 +- .../01-shallow-equality/index.ts | 15 ++- .../02-shallow-differences/index.test.ts | 2 +- .../02-shallow-differences/index.ts | 15 ++- .../03-deep-equality/index.test.ts | 2 +- .../analyzing-dna/03-deep-equality/index.ts | 21 +++- .../04-deep-differences/index.test.ts | 2 +- .../04-deep-differences/index.ts | 26 +++- .../arrays/text-processor/src/index.test.ts | 2 +- projects/arrays/text-processor/src/index.ts | 82 +++++++++++- .../01-polly-parrots-properties/index.test.ts | 2 +- .../01-polly-parrots-properties/index.ts | 23 +++- .../02-dog-displays/index.test.ts | 2 +- .../02-dog-displays/index.ts | 22 +++- .../03-hamster-helpers/index.test.ts | 2 +- .../03-hamster-helpers/index.ts | 52 +++++++- .../classes/horror-factory/src/index.test.ts | 2 +- projects/classes/horror-factory/src/index.ts | 69 +++++++++- .../the-shape-of-types/src/index.test.ts | 4 +- .../classes/the-shape-of-types/src/index.ts | 69 +++++++++- .../01-encoded-outputs/index.test.ts | 37 ++++++ .../01-encoded-outputs/tsconfig.json | 6 + .../02-allowed-imports/index.test.ts | 31 +++++ .../02-allowed-imports/tsconfig.json | 6 +- .../strictly-speaking/src/index.ts | 8 +- .../01-passport-declarations/index.ts | 24 ++-- .../02-unexpected-methods/index.ts | 11 ++ .../type-illusions/src/index.ts | 24 ++-- .../type-illusions/src/show.d.ts | 43 +++++++ .../01-syntactic-sugar/index.js | 20 ++- .../02-prototypes-to-classes/index.js | 49 +++++++- .../03-callbacks-to-async-await/index.js | 18 ++- .../01-incoming-cipher/index.test.ts | 2 +- .../01-incoming-cipher/index.ts | 15 ++- .../secret-secrets/02-dr-on/index.test.ts | 2 +- .../secret-secrets/02-dr-on/index.ts | 25 +++- .../03-the-golden-code/index.test.ts | 2 +- .../03-the-golden-code/index.ts | 24 +++- .../structural-kitchen/src/index.test.ts | 2 +- .../functions/structural-kitchen/src/index.ts | 92 +++++++++++++- .../generics/hidash/01-unique/index.test.ts | 2 +- projects/generics/hidash/01-unique/index.ts | 13 +- projects/generics/hidash/02-zip/index.test.ts | 2 +- projects/generics/hidash/02-zip/index.ts | 19 ++- .../generics/hidash/03-pick/index.test.ts | 2 +- projects/generics/hidash/03-pick/index.ts | 5 +- .../generics/hidash/04-pickmany/index.test.ts | 2 +- projects/generics/hidash/04-pickmany/index.ts | 11 +- .../treasure-hunter/src/index.test.ts | 2 +- .../generics/treasure-hunter/src/index.ts | 72 ++++++++++- .../playlist-soundness/src/index.test.ts | 2 +- .../playlist-soundness/src/index.ts | 67 +++++++++- .../vacation-planning/01-cities/index.test.ts | 2 +- .../vacation-planning/01-cities/index.ts | 44 ++++++- .../02-restaurants/index.test.ts | 2 +- .../vacation-planning/02-restaurants/index.ts | 24 +++- .../03-landmarks/index.test.ts | 2 +- .../vacation-planning/03-landmarks/index.ts | 88 ++++++++++++- projects/objects/the-typer/src/index.ts | 53 +++++++- .../01-aviary-classification/index.ts | 8 +- .../02-case-management/index.ts | 15 +-- .../03-making-arguments/index.ts | 38 +++++- .../01-whats-in-a-namespace/index.ts | 8 +- .../01-whats-in-a-namespace/poetry.ts | 118 +++++++++--------- .../01-whats-in-a-namespace/rhymes.ts | 30 +++-- .../02-much-ado-about-enums/colors.ts | 12 +- .../02-much-ado-about-enums/index.ts | 17 ++- .../02-much-ado-about-enums/rhymes.ts | 8 +- .../03-typus-andronicus/conflict.ts | 2 +- .../typespeare/03-typus-andronicus/index.ts | 12 +- .../03-typus-andronicus/resolution.ts | 4 +- .../01-clowning-around/index.ts | 8 +- .../02-clown-availability/index.ts | 14 +-- .../01-fabulous-secret-powers/index.test.ts | 2 +- .../01-fabulous-secret-powers/index.ts | 17 ++- .../02-artifact-assertions/index.test.ts | 2 +- .../02-artifact-assertions/index.ts | 11 +- .../type-force/src/index.test.ts | 2 +- .../type-modifiers/type-force/src/index.ts | 42 +++++-- .../01-flat-filter/index.ts | 9 +- .../02-reverse/index.ts | 5 +- .../array-type-shenanigans/03-zip/index.ts | 10 +- .../01-spongecase/index.ts | 10 +- .../02-word-replace/index.ts | 9 +- .../03-split-on/index.ts | 9 +- .../primitive-cooking/01-ingredients/index.ts | 6 +- .../primitive-cooking/02-recipes/index.ts | 6 +- .../primitive-cooking/03-seating/index.ts | 13 +- .../the-narrow-trail/src/index.test.ts | 2 +- .../the-narrow-trail/src/index.ts | 60 ++++++++- .../temporary-code/src/Person.ts | 5 + .../temporary-code/src/criticizePerson.ts | 7 ++ .../temporary-code/src/index.test.ts | 2 +- .../temporary-code/src/index.ts | 19 +-- .../01-favorite-animals/index.test.ts | 2 +- .../typearium/01-favorite-animals/index.ts | 40 +++--- .../typearium/02-species-collections/fauna.ts | 11 ++ .../02-species-collections/fauna/mammals.ts | 17 +++ .../02-species-collections/fauna/reptiles.ts | 13 ++ .../typearium/02-species-collections/flora.ts | 11 ++ .../02-species-collections/flora/flowers.ts | 13 ++ .../02-species-collections/flora/trees.ts | 13 ++ .../02-species-collections/index.test.ts | 2 +- .../typearium/02-species-collections/index.ts | 92 +------------- .../utils/onlyTruthy.ts | 19 +++ scripts/copy-solutions.mjs | 2 +- 106 files changed, 1644 insertions(+), 404 deletions(-) create mode 100644 projects/configuration-options/secret-messages/01-encoded-outputs/index.test.ts create mode 100644 projects/configuration-options/secret-messages/02-allowed-imports/index.test.ts create mode 100644 projects/using-ide-features/temporary-code/src/Person.ts create mode 100644 projects/using-ide-features/temporary-code/src/criticizePerson.ts create mode 100644 projects/using-ide-features/typearium/02-species-collections/fauna.ts create mode 100644 projects/using-ide-features/typearium/02-species-collections/fauna/mammals.ts create mode 100644 projects/using-ide-features/typearium/02-species-collections/fauna/reptiles.ts create mode 100644 projects/using-ide-features/typearium/02-species-collections/flora.ts create mode 100644 projects/using-ide-features/typearium/02-species-collections/flora/flowers.ts create mode 100644 projects/using-ide-features/typearium/02-species-collections/flora/trees.ts create mode 100644 projects/using-ide-features/typearium/02-species-collections/utils/onlyTruthy.ts diff --git a/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts b/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts index ca84712d..8135db42 100644 --- a/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts +++ b/projects/arrays/analyzing-dna/01-shallow-equality/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { shallowEquality } = process.env.TEST_SOLUTIONS ? solution : index; +const { shallowEquality } = process.env.TEST_SOLUTIONS ? index : index; describe(shallowEquality, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/01-shallow-equality/index.ts b/projects/arrays/analyzing-dna/01-shallow-equality/index.ts index e812c459..d04ec3da 100644 --- a/projects/arrays/analyzing-dna/01-shallow-equality/index.ts +++ b/projects/arrays/analyzing-dna/01-shallow-equality/index.ts @@ -1,2 +1,13 @@ -// Write your shallowEquality function here! ✨ -// You'll need to export it so the tests can run it. +export function shallowEquality(a: string[], b: string[]) { + if (a.length !== b.length) { + return false; + } + + for (let i = 0; i < a.length; i += 1) { + if (a[i] !== b[i]) { + return false; + } + } + + return true; +} diff --git a/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts b/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts index cbe76ac6..d93ee019 100644 --- a/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts +++ b/projects/arrays/analyzing-dna/02-shallow-differences/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { shallowDifferences } = process.env.TEST_SOLUTIONS ? solution : index; +const { shallowDifferences } = process.env.TEST_SOLUTIONS ? index : index; describe(shallowDifferences, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/02-shallow-differences/index.ts b/projects/arrays/analyzing-dna/02-shallow-differences/index.ts index e97b7cbe..035f7e24 100644 --- a/projects/arrays/analyzing-dna/02-shallow-differences/index.ts +++ b/projects/arrays/analyzing-dna/02-shallow-differences/index.ts @@ -1,2 +1,13 @@ -// Write your shallowDifferences function here! ✨ -// You'll need to export it so the tests can run it. +export function shallowDifferences(a: string[], b: string[]) { + if (a.length !== b.length) { + return undefined; + } + + const result: (string | undefined)[] = []; + + for (let i = 0; i < a.length; i += 1) { + result.push(a[i] === b[i] ? a[i] : undefined); + } + + return result; +} diff --git a/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts b/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts index b675c522..e143020f 100644 --- a/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts +++ b/projects/arrays/analyzing-dna/03-deep-equality/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { deepEquality } = process.env.TEST_SOLUTIONS ? solution : index; +const { deepEquality } = process.env.TEST_SOLUTIONS ? index : index; describe(deepEquality, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/03-deep-equality/index.ts b/projects/arrays/analyzing-dna/03-deep-equality/index.ts index cd270fd1..ec2b6d28 100644 --- a/projects/arrays/analyzing-dna/03-deep-equality/index.ts +++ b/projects/arrays/analyzing-dna/03-deep-equality/index.ts @@ -1,2 +1,19 @@ -// Write your deepEquality function here! ✨ -// You'll need to export it so the tests can run it. +export function deepEquality(a: string[][], b: string[][]) { + if (a.length !== b.length) { + return false; + } + + for (let i = 0; i < a.length; i += 1) { + if (a[i].length !== b[i].length) { + return false; + } + + for (let j = 0; j < a[i].length; j += 1) { + if (a[i][j] !== b[i][j]) { + return false; + } + } + } + + return true; +} diff --git a/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts b/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts index 321f8850..a071a2ab 100644 --- a/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts +++ b/projects/arrays/analyzing-dna/04-deep-differences/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { deepDifferences } = process.env.TEST_SOLUTIONS ? solution : index; +const { deepDifferences } = process.env.TEST_SOLUTIONS ? index : index; describe(deepDifferences, () => { test.each([ diff --git a/projects/arrays/analyzing-dna/04-deep-differences/index.ts b/projects/arrays/analyzing-dna/04-deep-differences/index.ts index 9eb139e1..a38e21e0 100644 --- a/projects/arrays/analyzing-dna/04-deep-differences/index.ts +++ b/projects/arrays/analyzing-dna/04-deep-differences/index.ts @@ -1,2 +1,24 @@ -// Write your deepDifferences function here! ✨ -// You'll need to export it so the tests can run it. +export function deepDifferences(a: string[][], b: string[][]) { + if (a.length !== b.length) { + return undefined; + } + + const allResults: ((string | undefined)[] | undefined)[] = []; + + for (let i = 0; i < a.length; i += 1) { + if (a[i].length !== b[i].length) { + allResults.push(undefined); + continue; + } + + const results: (string | undefined)[] = []; + + for (let j = 0; j < a[i].length; j += 1) { + results.push(a[i][j] === b[i][j] ? a[i][j] : undefined); + } + + allResults.push(results); + } + + return allResults; +} diff --git a/projects/arrays/text-processor/src/index.test.ts b/projects/arrays/text-processor/src/index.test.ts index 397332a5..deead27a 100644 --- a/projects/arrays/text-processor/src/index.test.ts +++ b/projects/arrays/text-processor/src/index.test.ts @@ -6,7 +6,7 @@ import * as solution from "./solution"; process.env.TEST_SOLUTIONS = "1"; -const { alignTexts } = process.env.TEST_SOLUTIONS ? solution : index; +const { alignTexts } = process.env.TEST_SOLUTIONS ? index : index; describe(alignTexts, () => { describe("types", () => { diff --git a/projects/arrays/text-processor/src/index.ts b/projects/arrays/text-processor/src/index.ts index afbfe05c..b980de44 100644 --- a/projects/arrays/text-processor/src/index.ts +++ b/projects/arrays/text-processor/src/index.ts @@ -1,2 +1,80 @@ -// Write your alignTexts function here! ✨ -// You'll need to export it so the tests can run it. +export type AlignmentOptions = { + align?: "left" | "middle" | "right"; + width: number; +}; + +export function alignTexts(texts: string[], options: AlignmentOptions) { + const alignedTextsLines: string[][] = []; + + for (const text of texts) { + const lines = splitLines(text, options.width); + const aligned = alignLines(lines, options); + + alignedTextsLines.push(aligned); + } + + return alignedTextsLines; +} + +function splitLines(text: string, width: number) { + const lines: string[] = []; + let line = ""; + + for (const word of text.split(" ")) { + if (line === "") { + line = word; + } else if (line.length + word.length < width) { + line += ` ${word}`; + } else { + lines.push(line); + line = word; + } + } + + lines.push(line); + + return lines; +} + +function alignLines( + lines: string[], + { align = "left", width }: AlignmentOptions +) { + const aligned: string[] = []; + + for (const line of lines) { + const remainingSpaces = width - line.length; + let newLine = line; + + if (remainingSpaces) { + switch (align) { + case "left": + for (let i = 0; i < remainingSpaces; i += 1) { + newLine += " "; + } + break; + + case "middle": + for (let i = 0; i < Math.ceil(remainingSpaces / 2); i += 1) { + newLine += " "; + } + + for (let i = 0; i < Math.floor(remainingSpaces / 2); i += 1) { + newLine = " " + newLine; + } + + break; + + case "right": + for (let i = 0; i < remainingSpaces; i += 1) { + newLine = " " + newLine; + } + break; + } + } + + aligned.push(newLine); + } + + return aligned; +} diff --git a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts index 7ed1e02a..5907de60 100644 --- a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts +++ b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Parrot } = process.env.TEST_SOLUTIONS ? solution : index; +const { Parrot } = process.env.TEST_SOLUTIONS ? index : index; const mockRandom = jest.spyOn(Math, "random"); diff --git a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.ts b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.ts index f59aea31..bff31f56 100644 --- a/projects/classes/classifying-creatures/01-polly-parrots-properties/index.ts +++ b/projects/classes/classifying-creatures/01-polly-parrots-properties/index.ts @@ -1,2 +1,21 @@ -// Write your Parrot class here! ✨ -// You'll need to export it so the tests can run it. +export class Parrot { + #name: string; + #phrases: string[]; + + constructor(name: string) { + this.#name = name; + this.#phrases = [`${name} wants a cracker!`]; + } + + announce() { + return `Squawk! I'm ${this.#name}!`; + } + + learn(phrase: string) { + this.#phrases.push(phrase); + } + + speak() { + return this.#phrases[Math.floor(Math.random() * this.#phrases.length)]; + } +} diff --git a/projects/classes/classifying-creatures/02-dog-displays/index.test.ts b/projects/classes/classifying-creatures/02-dog-displays/index.test.ts index 68175edc..3479e9af 100644 --- a/projects/classes/classifying-creatures/02-dog-displays/index.test.ts +++ b/projects/classes/classifying-creatures/02-dog-displays/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Puppy } = process.env.TEST_SOLUTIONS ? solution : index; +const { Puppy } = process.env.TEST_SOLUTIONS ? index : index; describe(Puppy, () => { describe("properties", () => { diff --git a/projects/classes/classifying-creatures/02-dog-displays/index.ts b/projects/classes/classifying-creatures/02-dog-displays/index.ts index eef68e77..62801953 100644 --- a/projects/classes/classifying-creatures/02-dog-displays/index.ts +++ b/projects/classes/classifying-creatures/02-dog-displays/index.ts @@ -1,2 +1,20 @@ -// Write your class and interface here! ✨ -// You'll need to export it so the tests can run it. +export interface PuppyInTheWindow { + readonly colors: string[]; + readonly furriness: number; + readonly owner: string | undefined; +} + +export class Puppy implements PuppyInTheWindow { + colors: string[]; + furriness: number; + owner: string | undefined; + + constructor(colors: string[], furriness: number) { + this.colors = colors; + this.furriness = furriness; + } + + adopt(owner: string) { + this.owner = owner; + } +} diff --git a/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts b/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts index aa8ac806..4907d1e0 100644 --- a/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts +++ b/projects/classes/classifying-creatures/03-hamster-helpers/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { Gerbil, Hamster } = process.env.TEST_SOLUTIONS ? solution : index; +const { Gerbil, Hamster } = process.env.TEST_SOLUTIONS ? index : index; describe(Gerbil, () => { describe("species", () => { diff --git a/projects/classes/classifying-creatures/03-hamster-helpers/index.ts b/projects/classes/classifying-creatures/03-hamster-helpers/index.ts index 11344e4d..1e92cf07 100644 --- a/projects/classes/classifying-creatures/03-hamster-helpers/index.ts +++ b/projects/classes/classifying-creatures/03-hamster-helpers/index.ts @@ -1,2 +1,50 @@ -// Write your type and classes here! ✨ -// You'll need to export the classes so the tests can run them. +export type SmallPetFood = + | "bugs" + | "fruits" + | "insects" + | "plants" + | "seeds" + | "vegetables"; + +export abstract class SmallFurryPet { + readonly species: string; + protected happiness = 0; + + constructor(species: string) { + this.species = species; + } + + abstract eats(food: SmallPetFood): boolean; + + isHappy(): boolean { + return this.happiness >= 1; + } +} + +export class Gerbil extends SmallFurryPet { + constructor() { + super("Gerbil"); + } + + dig() { + this.happiness += 1; + } + + eats(food: SmallPetFood) { + return ["insects", "plants", "seeds", "vegetables"].includes(food); + } +} + +export class Hamster extends SmallFurryPet { + constructor() { + super("Hamster"); + } + + eats() { + return true; + } + + run() { + this.happiness += 1; + } +} diff --git a/projects/classes/horror-factory/src/index.test.ts b/projects/classes/horror-factory/src/index.test.ts index 14005c39..84c2e7d2 100644 --- a/projects/classes/horror-factory/src/index.test.ts +++ b/projects/classes/horror-factory/src/index.test.ts @@ -4,7 +4,7 @@ import * as index from "./index"; import * as solution from "./solution"; const { createDemon, createSorcerer, Horror } = process.env.TEST_SOLUTIONS - ? solution + ? index : index; const createMockHorrorSettings = (evil: boolean) => { diff --git a/projects/classes/horror-factory/src/index.ts b/projects/classes/horror-factory/src/index.ts index 96c26f62..bed70b3d 100644 --- a/projects/classes/horror-factory/src/index.ts +++ b/projects/classes/horror-factory/src/index.ts @@ -1,2 +1,67 @@ -// Write your class and functions here! ✨ -// You'll need to export the class and functions so the tests can run it. +interface Consumed { + evil: boolean; + name: string; + power: number; +} + +interface HorrorSettings { + name: string; + getPowerFrom: (consumed: Consumed) => number; + isEvil: () => boolean; +} + +export class Horror { + #consumed: Consumed[] = []; + + readonly isEvil: () => boolean; + readonly name: string; + readonly #getPowerFrom: (consumed: Consumed) => number; + + constructor({ name, getPowerFrom, isEvil }: HorrorSettings) { + this.isEvil = isEvil; + this.name = name; + this.#getPowerFrom = getPowerFrom; + } + + #consume(opponent: Horror) { + this.#consumed.push({ + evil: opponent.isEvil(), + name: opponent.name, + power: opponent.getPower(), + }); + } + + doBattle(opponent: Horror) { + if (this.getPower() >= opponent.getPower()) { + this.#consume(opponent); + } + } + + getPower() { + return this.#consumed.reduce( + (previous, consumed) => previous + this.#getPowerFrom(consumed), + this.#consumed.length + ); + } +} + +const demonSettings: HorrorSettings = { + name: "Demon", + getPowerFrom: (consumed: Consumed) => { + return consumed.evil ? consumed.power / 2 : consumed.power * 2; + }, + isEvil: () => true, +}; + +export function createDemon() { + return new Horror(demonSettings); +} + +export function createSorcerer(name: string, evil: boolean) { + return new Horror({ + name, + getPowerFrom: (consumed: Consumed) => + consumed.evil === evil ? consumed.power * 2 : consumed.power, + isEvil: () => evil, + }); +} diff --git a/projects/classes/the-shape-of-types/src/index.test.ts b/projects/classes/the-shape-of-types/src/index.test.ts index 9569aea4..19e3b543 100644 --- a/projects/classes/the-shape-of-types/src/index.test.ts +++ b/projects/classes/the-shape-of-types/src/index.test.ts @@ -4,11 +4,11 @@ import * as index from "./index"; import * as solution from "./solution"; const { Demon, Horror, Sorcerer } = process.env.TEST_SOLUTIONS - ? solution + ? index : // In theory, it would be nice to not have to apply this cast // In practice, TypeScript's structural typing does not play well with # privates // See https://github.com/LearningTypeScript/projects/issues/183 - (index as unknown as typeof solution); + (index as unknown as typeof index); class MockHorror extends Horror { name = ""; diff --git a/projects/classes/the-shape-of-types/src/index.ts b/projects/classes/the-shape-of-types/src/index.ts index 9056875a..0d754b1d 100644 --- a/projects/classes/the-shape-of-types/src/index.ts +++ b/projects/classes/the-shape-of-types/src/index.ts @@ -1,2 +1,67 @@ -// Write your classes here! ✨ -// You'll need to export them so the tests can run them. +interface Consumed { + evil: boolean; + name: string; + power: number; +} + +export abstract class Horror { + #consumed: Consumed[] = []; + + abstract readonly name: string; + + #consume(opponent: Horror) { + this.#consumed.push({ + evil: opponent.isEvil(), + name: opponent.name, + power: opponent.getPower(), + }); + } + + doBattle(opponent: Horror) { + if (this.getPower() >= opponent.getPower()) { + this.#consume(opponent); + } + } + + getPower() { + return this.#consumed.reduce( + (previous, consumed) => previous + this.getPowerFrom(consumed), + this.#consumed.length + ); + } + + protected abstract getPowerFrom(consumed: Consumed): number; + + protected abstract isEvil(): boolean; +} + +export class Demon extends Horror { + readonly name = "Demon"; + + getPowerFrom(consumed: Consumed) { + return consumed.evil ? consumed.power / 2 : consumed.power * 2; + } + + isEvil() { + return true; + } +} + +export class Sorcerer extends Horror { + readonly name: string; + #evil: boolean; + + constructor(name: string, evil: boolean) { + super(); + this.name = name; + this.#evil = evil; + } + + getPowerFrom(consumed: Consumed) { + return consumed.evil === this.#evil ? consumed.power * 2 : consumed.power; + } + + isEvil() { + return this.#evil; + } +} diff --git a/projects/configuration-options/secret-messages/01-encoded-outputs/index.test.ts b/projects/configuration-options/secret-messages/01-encoded-outputs/index.test.ts new file mode 100644 index 00000000..c3c1ce6d --- /dev/null +++ b/projects/configuration-options/secret-messages/01-encoded-outputs/index.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, test } from "@jest/globals"; +import * as fs from "fs"; +import * as path from "path"; +import * as ts from "typescript"; + +const filePath = path.join( + __dirname, + process.env.TEST_SOLUTIONS ? "tsconfig.solution.json" : "tsconfig.json" +); + +const { config, error } = ts.parseConfigFileTextToJson( + filePath, + fs.readFileSync(filePath).toString() +); + +if (error) { + console.error(error); + throw new Error("Could not parse TSConfig for test. See console errors."); +} + +describe("TSConfig", () => { + test("compilerOptions", () => { + expect(config.compilerOptions).toEqual( + expect.objectContaining({ + declaration: true, + declarationMap: true, + sourceMap: true, + }) + ); + }); + + test("exclude", () => { + expect(config.exclude).toEqual( + expect.arrayContaining([expect.stringMatching(/verify/)]) + ); + }); +}); diff --git a/projects/configuration-options/secret-messages/01-encoded-outputs/tsconfig.json b/projects/configuration-options/secret-messages/01-encoded-outputs/tsconfig.json index f9927a1b..67e0e488 100644 --- a/projects/configuration-options/secret-messages/01-encoded-outputs/tsconfig.json +++ b/projects/configuration-options/secret-messages/01-encoded-outputs/tsconfig.json @@ -1,4 +1,10 @@ { // Write your configuration options here! ✨ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "exclude": ["*.verify.*"], "include": ["."] } diff --git a/projects/configuration-options/secret-messages/02-allowed-imports/index.test.ts b/projects/configuration-options/secret-messages/02-allowed-imports/index.test.ts new file mode 100644 index 00000000..21c14bb2 --- /dev/null +++ b/projects/configuration-options/secret-messages/02-allowed-imports/index.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from "@jest/globals"; +import * as fs from "fs"; +import * as path from "path"; +import * as ts from "typescript"; + +const filePath = path.join( + __dirname, + process.env.TEST_SOLUTIONS ? "tsconfig.solution.json" : "tsconfig.json" +); + +const { config, error } = ts.parseConfigFileTextToJson( + filePath, + fs.readFileSync(filePath).toString() +); + +if (error) { + console.error(error); + throw new Error("Could not parse TSConfig for test. See console errors."); +} + +describe("TSConfig", () => { + test("compilerOptions", () => { + expect(config.compilerOptions).toEqual( + expect.objectContaining({ + allowJs: true, + esModuleInterop: true, + resolveJsonModule: true, + }) + ); + }); +}); diff --git a/projects/configuration-options/secret-messages/02-allowed-imports/tsconfig.json b/projects/configuration-options/secret-messages/02-allowed-imports/tsconfig.json index f9927a1b..e053021d 100644 --- a/projects/configuration-options/secret-messages/02-allowed-imports/tsconfig.json +++ b/projects/configuration-options/secret-messages/02-allowed-imports/tsconfig.json @@ -1,4 +1,8 @@ { // Write your configuration options here! ✨ - "include": ["."] + "compilerOptions": { + "allowJs": true, + "esModuleInterop": true, + "resolveJsonModule": true + } } diff --git a/projects/configuration-options/strictly-speaking/src/index.ts b/projects/configuration-options/strictly-speaking/src/index.ts index 0ffc4037..292848b6 100644 --- a/projects/configuration-options/strictly-speaking/src/index.ts +++ b/projects/configuration-options/strictly-speaking/src/index.ts @@ -2,11 +2,11 @@ import { words } from "./words"; -async function seconds(amount) { +async function seconds(amount: number) { await new Promise((resolve) => setTimeout(resolve, amount * 1000)); } -async function speakLines(lines: (number | string)[]) { +async function speakLines(lines: (number | string | undefined)[]) { for (const line of lines) { if (typeof line === "string") { console.log(line); @@ -18,7 +18,7 @@ async function speakLines(lines: (number | string)[]) { } function generateLines(quantity: number) { - const lines = [generateLine("Lorem ipsum")]; + const lines: (number | string | undefined)[] = [generateLine("Lorem ipsum")]; for (let i = 0; i < quantity - 1; i += 1) { lines.push(generateLine()); @@ -31,7 +31,7 @@ function generateLines(quantity: number) { return lines; } -function generateLine(prefix) { +function generateLine(prefix?: string) { const words: string[] = prefix ? [prefix] : []; const quantity = randomInt(3, 10); diff --git a/projects/declaration-files/global-travels/01-passport-declarations/index.ts b/projects/declaration-files/global-travels/01-passport-declarations/index.ts index e58d13dd..ae3bca39 100644 --- a/projects/declaration-files/global-travels/01-passport-declarations/index.ts +++ b/projects/declaration-files/global-travels/01-passport-declarations/index.ts @@ -1,5 +1,20 @@ // Write your types here! ✨ +declare global { + interface Window { + passports: Passports; + } +} + +interface Passports { + [i: string]: Passport | undefined; +} + +interface Passport { + expires: Date; + name: string; +} + export function checkPassport(id: string) { const passport = window.passports[id]; @@ -10,17 +25,10 @@ export function checkPassport(id: string) { }; } - if (!passport.name) { - return { - allowed: false, - reason: "No known name.", - }; - } - if (passport.expires.getTime() < new Date().getTime()) { return { allowed: false, - reason: "Passport has expired.", + reason: `Passport for ${passport.name} has expired.`, }; } diff --git a/projects/declaration-files/global-travels/02-unexpected-methods/index.ts b/projects/declaration-files/global-travels/02-unexpected-methods/index.ts index 89dd5388..ebb98d25 100644 --- a/projects/declaration-files/global-travels/02-unexpected-methods/index.ts +++ b/projects/declaration-files/global-travels/02-unexpected-methods/index.ts @@ -1,5 +1,16 @@ // Write your types here! ✨ +declare global { + interface Array { + smoosh(): string; + unsmoosh(): string[]; + } + + interface String { + alternating(): [string, string]; + } +} + export function logMessage(): string { const encodedMessage = "wjheiwcehljearv'assfcarvioprtiptrei?msitt-irvienigssa!"; diff --git a/projects/declaration-files/type-illusions/src/index.ts b/projects/declaration-files/type-illusions/src/index.ts index 3c2851a3..dfb7ba4a 100644 --- a/projects/declaration-files/type-illusions/src/index.ts +++ b/projects/declaration-files/type-illusions/src/index.ts @@ -1,12 +1,19 @@ -// Add back imports and type annotations here! ✨ +import { + Act, + AudienceMember, + getAudienceMemberFor, + Illusion, + isTrick, + isVolunteerIllusion, + Trick, + VolunteerIllusion, +} from "./show.solution"; -import { getAudienceMemberFor, isTrick, isVolunteerIllusion } from "./show"; - -function showTrick(trick) { +function showTrick(trick: Trick) { console.log(`💨 Voila! ${trick.gimmick}`); } -function showIllusion(illusion) { +function showIllusion(illusion: Illusion) { console.log("This is not a trick..."); console.log(`\t${illusion.introduction()}`); @@ -17,7 +24,10 @@ function showIllusion(illusion) { console.log(`\t${illusion.payoff()}`); } -async function showVolunteerIllusion(audienceMember, illusion) { +async function showVolunteerIllusion( + audienceMember: AudienceMember, + illusion: VolunteerIllusion +) { console.log( `Let's give a big round of applause for ${audienceMember.name}! 👏` ); @@ -31,7 +41,7 @@ async function showVolunteerIllusion(audienceMember, illusion) { showIllusion(illusion); } -export async function runShow(act) { +export async function runShow(act: Act) { console.log(`Hello! I am the amazing ${act.performer}, and this...`); console.log(`...is ${act.name}! ✨`); diff --git a/projects/declaration-files/type-illusions/src/show.d.ts b/projects/declaration-files/type-illusions/src/show.d.ts index 845c77f5..4aeaa14f 100644 --- a/projects/declaration-files/type-illusions/src/show.d.ts +++ b/projects/declaration-files/type-illusions/src/show.d.ts @@ -1 +1,44 @@ // Declare your types here! ✨ + +export interface Act { + performer: string; + name: string; + sections: Section[]; +} + +export type Section = Illusion | Trick; + +export interface Illusion { + introduction(): string; + flair(): string; + payoff(): string; +} + +export interface VolunteerIllusion extends Illusion { + duration: number; + title: string; +} + +export interface Trick { + gimmick: string; +} + +export function isTrick(section: Section): section is Trick; + +export function isVolunteerIllusion( + illusion: Illusion +): illusion is VolunteerIllusion; + +export interface GetAudienceMemberSettings { + duration: number; + title: string; +} + +export interface AudienceMember { + name: string; + willing: boolean; +} + +export function getAudienceMemberFor( + settings: GetAudienceMemberSettings +): Promise; diff --git a/projects/from-javascript-to-typescript/the-typeinator/01-syntactic-sugar/index.js b/projects/from-javascript-to-typescript/the-typeinator/01-syntactic-sugar/index.js index 7da3edce..c52257bf 100644 --- a/projects/from-javascript-to-typescript/the-typeinator/01-syntactic-sugar/index.js +++ b/projects/from-javascript-to-typescript/the-typeinator/01-syntactic-sugar/index.js @@ -1,4 +1,20 @@ -// Put your announceMachines function here! ✨ -// See ./original.js for its older JavaScript code. +function announceMachines(announce, ...machines) { + let labelsCount = 0; + + for (const machine of machines) { + let label; + + if (machine.label) { + label = machine.label; + labelsCount += 1; + } else { + label = `Make: ${machine.make}; Model: ${machine.model}`; + } + + announce(label); + } + + return labelsCount; +} module.exports.announceMachines = announceMachines; diff --git a/projects/from-javascript-to-typescript/the-typeinator/02-prototypes-to-classes/index.js b/projects/from-javascript-to-typescript/the-typeinator/02-prototypes-to-classes/index.js index ed667c9a..e2c9cc27 100644 --- a/projects/from-javascript-to-typescript/the-typeinator/02-prototypes-to-classes/index.js +++ b/projects/from-javascript-to-typescript/the-typeinator/02-prototypes-to-classes/index.js @@ -1,5 +1,50 @@ -// Put your Robot and Humanoid classes here! ✨ -// See ./original.js for their older JavaScript code. +class Robot { + constructor(name, abilities) { + this.name = name; + this.abilities = abilities; + this.power = 100; + } + + announce() { + console.log("Greetings. I am " + this.name + "."); + + for (var i = 0; i < this.abilities.length; i += 1) { + console.log("I am able to " + this.abilities[i] + "."); + } + } + + charge(amount) { + if (this.power < 100) { + this.power = Math.min(this.power + amount, 100); + console.log("Recharged power supplies to " + this.power + "."); + } + + if (this.power === 100) { + console.log("I am at optimal operational capacity."); + } + } + + move(distance) { + if (this.power < distance) { + console.log("I do not have enough power to move " + distance + " units."); + } else { + console.log("Moving " + distance + " units."); + this.power -= distance; + } + } +} + +class Humanoid extends Robot { + constructor(name, abilities, catchphrase) { + super(name, abilities); + this.catchphrase = catchphrase; + } + + announce() { + super.announce(); + console.log(" > " + this.catchphrase + " <"); + } +} module.exports.Humanoid = Humanoid; module.exports.Robot = Robot; diff --git a/projects/from-javascript-to-typescript/the-typeinator/03-callbacks-to-async-await/index.js b/projects/from-javascript-to-typescript/the-typeinator/03-callbacks-to-async-await/index.js index c5f50a2b..b4a8a8ee 100644 --- a/projects/from-javascript-to-typescript/the-typeinator/03-callbacks-to-async-await/index.js +++ b/projects/from-javascript-to-typescript/the-typeinator/03-callbacks-to-async-await/index.js @@ -1,5 +1,19 @@ -// Put your checkEmotion and speak functions here! ✨ -// See ./original.js for their older JavaScript code. +async function checkEmotion(knownEmotions, emotion) { + // Simulate database processing time by waiting a second... + await new Promise((resolve) => { + return setTimeout(resolve, 1000); + }); + + return knownEmotions.has(emotion); +} + +async function speak(knownEmotions, newEmotion, phrase) { + if (!(await checkEmotion(knownEmotions, newEmotion))) { + throw new Error(`Does not compute. I do not understand ${newEmotion}.`); + } + + return `"${phrase}" (${newEmotion})`; +} module.exports.checkEmotion = checkEmotion; module.exports.speak = speak; diff --git a/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts b/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts index 6549f1d7..f2b56a16 100644 --- a/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts +++ b/projects/functions/secret-secrets/01-incoming-cipher/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createCipher } = process.env.TEST_SOLUTIONS ? solution : index; +const { createCipher } = process.env.TEST_SOLUTIONS ? index : index; describe(createCipher, () => { describe("types", () => { diff --git a/projects/functions/secret-secrets/01-incoming-cipher/index.ts b/projects/functions/secret-secrets/01-incoming-cipher/index.ts index f026c80d..59f9535d 100644 --- a/projects/functions/secret-secrets/01-incoming-cipher/index.ts +++ b/projects/functions/secret-secrets/01-incoming-cipher/index.ts @@ -1,2 +1,13 @@ -// Write your createCipher function here! ✨ -// You'll need to export it so the tests can run it. +export type Cipher = (character: string) => string; + +export function createCipher(cipher: Cipher) { + return (text: string) => { + let result = ""; + + for (const character of text) { + result += cipher(character); + } + + return result; + }; +} diff --git a/projects/functions/secret-secrets/02-dr-on/index.test.ts b/projects/functions/secret-secrets/02-dr-on/index.test.ts index 5b52a16d..85636c1c 100644 --- a/projects/functions/secret-secrets/02-dr-on/index.test.ts +++ b/projects/functions/secret-secrets/02-dr-on/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createAdvancedCipher } = process.env.TEST_SOLUTIONS ? solution : index; +const { createAdvancedCipher } = process.env.TEST_SOLUTIONS ? index : index; describe(createAdvancedCipher, () => { describe("types", () => { diff --git a/projects/functions/secret-secrets/02-dr-on/index.ts b/projects/functions/secret-secrets/02-dr-on/index.ts index 89c04d64..b9307ed5 100644 --- a/projects/functions/secret-secrets/02-dr-on/index.ts +++ b/projects/functions/secret-secrets/02-dr-on/index.ts @@ -1,2 +1,23 @@ -// Write your createAdvancedCipher function here! ✨ -// You'll need to export it so the tests can run it. +export type Cipher = (character: string) => string; + +export function createAdvancedCipher( + onVowel: Cipher, + onConsonant: Cipher, + onPunctuation: Cipher +) { + return (text: string) => { + let result = ""; + + for (const character of text) { + const cipher = /[aeiou]/i.test(character) + ? onVowel + : /[bcdfghjklmnpqrstvwxyz]/i.test(character) + ? onConsonant + : onPunctuation; + + result += cipher(character); + } + + return result; + }; +} diff --git a/projects/functions/secret-secrets/03-the-golden-code/index.test.ts b/projects/functions/secret-secrets/03-the-golden-code/index.test.ts index e47bc634..fd8a6368 100644 --- a/projects/functions/secret-secrets/03-the-golden-code/index.test.ts +++ b/projects/functions/secret-secrets/03-the-golden-code/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { createCodeCracker } = process.env.TEST_SOLUTIONS ? solution : index; +const { createCodeCracker } = process.env.TEST_SOLUTIONS ? index : index; describe(createCodeCracker, () => { describe("types", () => { diff --git a/projects/functions/secret-secrets/03-the-golden-code/index.ts b/projects/functions/secret-secrets/03-the-golden-code/index.ts index 607bccb0..44ed530b 100644 --- a/projects/functions/secret-secrets/03-the-golden-code/index.ts +++ b/projects/functions/secret-secrets/03-the-golden-code/index.ts @@ -1,2 +1,22 @@ -// Write your createCodeCracker function here! ✨ -// You'll need to export it so the tests can run it. +export type CodeCrackerOptions = { + attempts: number; + makeGuess: (text: string, attempt: number) => string; + validateGuess: (guess: string) => boolean; +}; + +export function createCodeCracker({ + attempts, + makeGuess, + validateGuess, +}: CodeCrackerOptions) { + return (text: string) => { + for (let i = 0; i < attempts; i += 1) { + const guess = makeGuess(text, i); + if (validateGuess(guess)) { + return guess; + } + } + + return undefined; + }; +} diff --git a/projects/functions/structural-kitchen/src/index.test.ts b/projects/functions/structural-kitchen/src/index.test.ts index 82329961..5096e1b9 100644 --- a/projects/functions/structural-kitchen/src/index.test.ts +++ b/projects/functions/structural-kitchen/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, jest } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { createKitchen } = process.env.TEST_SOLUTIONS ? solution : index; +const { createKitchen } = process.env.TEST_SOLUTIONS ? index : index; describe(createKitchen, () => { describe("announce", () => { diff --git a/projects/functions/structural-kitchen/src/index.ts b/projects/functions/structural-kitchen/src/index.ts index 7d8c594c..bcf17dcb 100644 --- a/projects/functions/structural-kitchen/src/index.ts +++ b/projects/functions/structural-kitchen/src/index.ts @@ -1,2 +1,90 @@ -// Write your createKitchen function here! ✨ -// You'll need to export it so the tests can run it. +export type Cleaner = (dirt: number, time?: number) => number; + +export type Ingredients = { + breads: number; + fruits: number; + sauces: number; + vegetables: number; +}; + +export type RecipeResultFailed = { + succeeded: false; +}; + +export type RecipeResultSucceeded = { + newStock: Ingredients; + succeeded: true; +}; + +export type RecipeResult = RecipeResultFailed | RecipeResultSucceeded; + +export type Supplier = (expense: number) => Ingredients; + +export type Recipe = (ingredients: Ingredients) => RecipeResult; + +export type Kitchen = { + announce(): string; + clean(time?: number): void; + purchase(expense: number): boolean; + prepare(recipe: Recipe): boolean; +}; + +export function createKitchen( + budget: number, + cleaner: Cleaner, + supplier: Supplier +): Kitchen { + let dirt = 0; + let stock = { + breads: 0, + fruits: 0, + sauces: 0, + vegetables: 0, + }; + + return { + announce() { + return [ + `I have ${dirt} much dirt`, + `${budget} budget`, + `${stock.breads} bread(s)`, + `${stock.fruits} fruit(s)`, + `${stock.sauces} sauce(s)`, + `and ${stock.vegetables} vegetable(s).`, + ].join(", "); + }, + clean(time?: number) { + dirt = cleaner(dirt, time); + }, + purchase(expense: number) { + if (budget < expense) { + return false; + } + + const ingredients = supplier(expense); + + stock.breads += ingredients.breads; + stock.fruits += ingredients.fruits; + stock.sauces += ingredients.sauces; + stock.vegetables += ingredients.vegetables; + + budget -= expense; + + return true; + }, + prepare(recipe: Recipe) { + if (dirt >= 100) { + return false; + } + + const result = recipe(stock); + dirt += 1; + + if (result.succeeded) { + stock = result.newStock; + } + + return result.succeeded; + }, + }; +} diff --git a/projects/generics/hidash/01-unique/index.test.ts b/projects/generics/hidash/01-unique/index.test.ts index afd55461..c148e207 100644 --- a/projects/generics/hidash/01-unique/index.test.ts +++ b/projects/generics/hidash/01-unique/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { unique } = process.env.TEST_SOLUTIONS ? solution : index; +const { unique } = process.env.TEST_SOLUTIONS ? index : index; describe(unique, () => { test.each([ diff --git a/projects/generics/hidash/01-unique/index.ts b/projects/generics/hidash/01-unique/index.ts index d2b669c0..13334971 100644 --- a/projects/generics/hidash/01-unique/index.ts +++ b/projects/generics/hidash/01-unique/index.ts @@ -1,2 +1,11 @@ -// Write your unique function here! ✨ -// You'll need to export it so the tests can run it. +export function unique(...allItems: T[][]) { + const found = new Set(); + + for (const items of allItems) { + for (const item of items) { + found.add(item); + } + } + + return Array.from(found); +} diff --git a/projects/generics/hidash/02-zip/index.test.ts b/projects/generics/hidash/02-zip/index.test.ts index 5ed475da..b715e028 100644 --- a/projects/generics/hidash/02-zip/index.test.ts +++ b/projects/generics/hidash/02-zip/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { zip } = process.env.TEST_SOLUTIONS ? solution : index; +const { zip } = process.env.TEST_SOLUTIONS ? index : index; describe(zip, () => { test("types", () => { diff --git a/projects/generics/hidash/02-zip/index.ts b/projects/generics/hidash/02-zip/index.ts index f84ddd36..842322c1 100644 --- a/projects/generics/hidash/02-zip/index.ts +++ b/projects/generics/hidash/02-zip/index.ts @@ -1,2 +1,17 @@ -// Write your zip function here! ✨ -// You'll need to export it so the tests can run it. +export function zip(a: A[], b: B[]) { + const result: (A | B)[] = []; + let i: number; + + for (i = 0; i < Math.min(a.length, b.length); i += 1) { + result.push(a[i]); + result.push(b[i]); + } + + for (const remaining of [a, b]) { + for (; i < remaining.length; i += 1) { + result.push(remaining[i]); + } + } + + return result; +} diff --git a/projects/generics/hidash/03-pick/index.test.ts b/projects/generics/hidash/03-pick/index.test.ts index 1e49a2fd..58cea558 100644 --- a/projects/generics/hidash/03-pick/index.test.ts +++ b/projects/generics/hidash/03-pick/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { pick } = process.env.TEST_SOLUTIONS ? solution : index; +const { pick } = process.env.TEST_SOLUTIONS ? index : index; describe(pick, () => { test("types", () => { diff --git a/projects/generics/hidash/03-pick/index.ts b/projects/generics/hidash/03-pick/index.ts index 1ac968a6..1ac20e48 100644 --- a/projects/generics/hidash/03-pick/index.ts +++ b/projects/generics/hidash/03-pick/index.ts @@ -1,2 +1,3 @@ -// Write your pick function here! ✨ -// You'll need to export it so the tests can run it. +export function pick(container: T, key: K) { + return container[key]; +} diff --git a/projects/generics/hidash/04-pickmany/index.test.ts b/projects/generics/hidash/04-pickmany/index.test.ts index f6e8dfb5..e04f6356 100644 --- a/projects/generics/hidash/04-pickmany/index.test.ts +++ b/projects/generics/hidash/04-pickmany/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { pickMany } = process.env.TEST_SOLUTIONS ? solution : index; +const { pickMany } = process.env.TEST_SOLUTIONS ? index : index; describe(pickMany, () => { describe("types", () => { diff --git a/projects/generics/hidash/04-pickmany/index.ts b/projects/generics/hidash/04-pickmany/index.ts index 619001f1..1fc831df 100644 --- a/projects/generics/hidash/04-pickmany/index.ts +++ b/projects/generics/hidash/04-pickmany/index.ts @@ -1,2 +1,9 @@ -// Write your pickMany function here! ✨ -// You'll need to export it so the tests can run it. +export function pickMany(container: T, keys: K[]) { + const result: T[K][] = []; + + for (const key of keys) { + result.push(container[key]); + } + + return result; +} diff --git a/projects/generics/treasure-hunter/src/index.test.ts b/projects/generics/treasure-hunter/src/index.test.ts index 5029b14c..8a603054 100644 --- a/projects/generics/treasure-hunter/src/index.test.ts +++ b/projects/generics/treasure-hunter/src/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { collectTreasure } = process.env.TEST_SOLUTIONS ? solution : index; +const { collectTreasure } = process.env.TEST_SOLUTIONS ? index : index; describe(collectTreasure, () => { describe("types", () => { diff --git a/projects/generics/treasure-hunter/src/index.ts b/projects/generics/treasure-hunter/src/index.ts index 440d0cde..454a58f4 100644 --- a/projects/generics/treasure-hunter/src/index.ts +++ b/projects/generics/treasure-hunter/src/index.ts @@ -1,2 +1,70 @@ -// Write your collectTreasure function here! ✨ -// You'll need to export it so the tests can run it. +export type Buried = Buried[] | NextArea | Treasure; + +export type NextArea = Catacomb | TunnelSystem; + +export interface Catacomb { + inside: Buried; + type: "catacomb"; +} + +export interface TunnelSystem { + entrances: Buried[]; + type: "tunnels"; +} + +export interface Treasure { + content: T; + type: "treasure"; +} + +export function collectTreasure< + Content, + Fake extends Content, + Real extends Content +>( + buried: Buried, + isFake: (item: Content) => item is Fake, + isReal: (item: Content) => item is Real +) { + const fake: Fake[] = []; + const real: Real[] = []; + const scrap: Content[] = []; + + function recurse(data: Buried) { + const collected = collectTreasure(data, isFake, isReal); + + fake.push(...collected.fake); + real.push(...collected.real); + scrap.push(...collected.scrap); + } + + if (buried instanceof Array) { + for (const data of buried) { + recurse(data); + } + } else { + switch (buried.type) { + case "treasure": + if (isFake(buried.content)) { + fake.push(buried.content); + } else if (isReal(buried.content)) { + real.push(buried.content); + } else { + scrap.push(buried.content); + } + break; + + case "tunnels": + for (const entrance of buried.entrances) { + recurse(entrance); + } + break; + + case "catacomb": + recurse(buried.inside); + break; + } + } + + return { fake, real, scrap }; +} diff --git a/projects/interfaces/playlist-soundness/src/index.test.ts b/projects/interfaces/playlist-soundness/src/index.test.ts index 441a88a3..91e79dd1 100644 --- a/projects/interfaces/playlist-soundness/src/index.test.ts +++ b/projects/interfaces/playlist-soundness/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { unrollPlaylist } = process.env.TEST_SOLUTIONS ? solution : index; +const { unrollPlaylist } = process.env.TEST_SOLUTIONS ? index : index; describe(unrollPlaylist, () => { test.each<[solution.PlaylistItem[], unknown]>([ diff --git a/projects/interfaces/playlist-soundness/src/index.ts b/projects/interfaces/playlist-soundness/src/index.ts index 1b86f524..32afa466 100644 --- a/projects/interfaces/playlist-soundness/src/index.ts +++ b/projects/interfaces/playlist-soundness/src/index.ts @@ -1,2 +1,65 @@ -// Write your unrollPlaylist function and types here! ✨ -// You'll need to export the function so the tests can run it. +export interface Song { + artist: string | string[]; + length: number; + name: string; + type: "song"; +} + +export interface Album { + songs: Song[]; + type: "album"; +} + +export interface Playlist { + resolve(): Song[]; + type: "playlist"; +} + +export type PlaylistItem = Album | Song | Playlist; + +export interface Artists { + [i: string]: string[]; +} + +export interface UnrolledPlaylist { + artists: Artists; + songs: string[]; + time: number; +} + +export function unrollPlaylist(items: PlaylistItem[]): UnrolledPlaylist { + const artists: Artists = {}; + const songs: string[] = []; + let time = 0; + + function addSong(song: Song) { + const songArtists = + typeof song.artist === "string" ? [song.artist] : song.artist; + + for (const artist of songArtists) { + artists[artist] ??= []; + artists[artist].push(song.name); + } + + time += song.length; + songs.push(song.name); + } + + for (const item of items) { + switch (item.type) { + case "song": + addSong(item); + break; + + case "album": + item.songs.forEach(addSong); + break; + + case "playlist": + item.resolve().forEach(addSong); + break; + } + } + + return { artists, songs, time }; +} diff --git a/projects/interfaces/vacation-planning/01-cities/index.test.ts b/projects/interfaces/vacation-planning/01-cities/index.test.ts index fedfc577..50be8d22 100644 --- a/projects/interfaces/vacation-planning/01-cities/index.test.ts +++ b/projects/interfaces/vacation-planning/01-cities/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { describeCity } = process.env.TEST_SOLUTIONS ? solution : index; +const { describeCity } = process.env.TEST_SOLUTIONS ? index : index; const dedent = (text: TemplateStringsArray) => text[0].replaceAll(/\n\s*/g, "\n").trim(); diff --git a/projects/interfaces/vacation-planning/01-cities/index.ts b/projects/interfaces/vacation-planning/01-cities/index.ts index f4dd89e6..2a0b4588 100644 --- a/projects/interfaces/vacation-planning/01-cities/index.ts +++ b/projects/interfaces/vacation-planning/01-cities/index.ts @@ -1,2 +1,42 @@ -// Write your describeCity function here! ✨ -// You'll need to export it so the tests can run it. +export interface City { + catchphrase?: string; + coordinates: Coordinates; + name: string; +} + +export interface Coordinates { + north: Coordinate; + west: Coordinate; +} + +export type Coordinate = [number, number, number]; + +function describeUnit(unit: number) { + return unit.toString().padStart(2, "0"); +} + +function describeCoordinate(coordinate: Coordinate) { + return [ + `${describeUnit(coordinate[0])}°`, + `${describeUnit(coordinate[1])}'`, + `${describeUnit(coordinate[2])}"`, + ].join(""); +} + +export function describeCity(city: City) { + const lines = [`${city.name}, New York`]; + + if (city.catchphrase) { + lines.push(`* "${city.catchphrase}"`); + } + + lines.push( + [ + `* Located at`, + `${describeCoordinate(city.coordinates.north)}N`, + `${describeCoordinate(city.coordinates.west)}W`, + ].join(" ") + ); + + return lines.join("\n"); +} diff --git a/projects/interfaces/vacation-planning/02-restaurants/index.test.ts b/projects/interfaces/vacation-planning/02-restaurants/index.test.ts index 4fa84222..034dac4b 100644 --- a/projects/interfaces/vacation-planning/02-restaurants/index.test.ts +++ b/projects/interfaces/vacation-planning/02-restaurants/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { groupRestaurants } = process.env.TEST_SOLUTIONS ? solution : index; +const { groupRestaurants } = process.env.TEST_SOLUTIONS ? index : index; describe(groupRestaurants, () => { test.each([ diff --git a/projects/interfaces/vacation-planning/02-restaurants/index.ts b/projects/interfaces/vacation-planning/02-restaurants/index.ts index 5e358bf7..d5e9491b 100644 --- a/projects/interfaces/vacation-planning/02-restaurants/index.ts +++ b/projects/interfaces/vacation-planning/02-restaurants/index.ts @@ -1,2 +1,22 @@ -// Write your groupRestaurants function here! ✨ -// You'll need to export it so the tests can run it. +export interface Restaurant { + city: string; + name: string; +} + +export interface GroupedRestaurants { + [i: string]: string[]; +} + +export function groupRestaurants(restaurants: Restaurant[]) { + const grouped: GroupedRestaurants = {}; + + for (const { city, name } of restaurants) { + if (city in grouped) { + grouped[city].push(name); + } else { + grouped[city] = [name]; + } + } + + return grouped; +} diff --git a/projects/interfaces/vacation-planning/03-landmarks/index.test.ts b/projects/interfaces/vacation-planning/03-landmarks/index.test.ts index 30420f41..1fc608ec 100644 --- a/projects/interfaces/vacation-planning/03-landmarks/index.test.ts +++ b/projects/interfaces/vacation-planning/03-landmarks/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { describeLandmark } = process.env.TEST_SOLUTIONS ? solution : index; +const { describeLandmark } = process.env.TEST_SOLUTIONS ? index : index; const dedent = (text: TemplateStringsArray) => text[0].replaceAll(/\n\s*/g, "\n").trim(); diff --git a/projects/interfaces/vacation-planning/03-landmarks/index.ts b/projects/interfaces/vacation-planning/03-landmarks/index.ts index 0ba3c940..ef8e133b 100644 --- a/projects/interfaces/vacation-planning/03-landmarks/index.ts +++ b/projects/interfaces/vacation-planning/03-landmarks/index.ts @@ -1,2 +1,86 @@ -// Write your describeLandmark function here! ✨ -// You'll need to export it so the tests can run it. +export interface BaseLandmark { + name: string; +} + +export interface Fort extends BaseLandmark { + type: "fort"; +} + +export interface Lake extends BaseLandmark { + miles: number; + type: "lake"; +} + +export interface Lighthouse extends BaseLandmark { + height: number; + lit: number; + type: "lighthouse"; +} + +export interface Park extends BaseLandmark { + acres: number; + type: "park"; +} + +export interface Mountain extends BaseLandmark { + height: number; + type: "mountain"; +} + +export interface River extends BaseLandmark { + depth: number; + length: number; + type: "river"; +} + +export interface Waterfall extends BaseLandmark { + height: number; + type: "waterfall"; +} + +export type Landmark = + | Fort + | Lake + | Lighthouse + | Mountain + | Park + | River + | Waterfall; + +export function describeLandmark(landmark: Landmark) { + const output: string[] = [ + `${landmark.name} is a ${landmark.type} in Upstate New York.`, + ]; + + switch (landmark.type) { + case "lake": + output.push(`It covers ${landmark.miles} square miles of water.`); + break; + + case "mountain": + output.push(`Its peak is ${landmark.height} feet high.`); + break; + + case "lighthouse": + output.push( + `It was first lit in ${landmark.lit} and is ${landmark.height} feet high.` + ); + break; + + case "park": + output.push(`It covers ${landmark.acres} square acres.`); + break; + + case "river": + output.push( + `It flows for ${landmark.length} miles and is ${landmark.depth} feet deep at its deepest.` + ); + break; + + case "waterfall": + output.push(`It is ${landmark.height} feet high.`); + break; + } + + return output.join("\n"); +} diff --git a/projects/objects/the-typer/src/index.ts b/projects/objects/the-typer/src/index.ts index 23606861..570b8994 100644 --- a/projects/objects/the-typer/src/index.ts +++ b/projects/objects/the-typer/src/index.ts @@ -1,6 +1,53 @@ // Write your types here! ✨ -let current = { +export type PlaceBase = { + name: string; + proximity: number; + treasure?: string; +}; + +export type Clearing = PlaceBase & { + through?: Place; + type: "clearing"; +}; + +export type Path = PlaceBase & { + shortcut?: Place; + through: Place; + type: "path"; +}; + +export type Town = PlaceBase & { + around?: Place; + through?: Place; + type: "town"; +}; + +export type StreamBase = PlaceBase & { + type: "stream"; +}; + +export type StreamEnd = StreamBase & { + area: "end"; + upstream: Place; +}; + +export type StreamBegin = StreamBase & { + area: "begin"; + downstream: Place; +}; + +export type StreamMiddle = StreamBase & { + area: "middle"; + downstream: Place; + upstream: Place; +}; + +export type Stream = StreamBegin | StreamEnd | StreamMiddle; + +export type Place = Clearing | Town | Path | Stream; + +let current: Place | undefined = { name: "Woesong Bridge", proximity: 100, through: { @@ -65,7 +112,7 @@ let current = { type: "path", }; -let treasure; +let treasure: string | undefined; while (current) { console.log(`At: ${current.name}`); @@ -101,9 +148,11 @@ while (current) { case "begin": current = current.downstream; break; + case "end": current = current.upstream; break; + case "middle": current = current.downstream.proximity < current.upstream.proximity diff --git a/projects/objects/various-lawyerings/01-aviary-classification/index.ts b/projects/objects/various-lawyerings/01-aviary-classification/index.ts index 86dfc933..1ef2d0b3 100644 --- a/projects/objects/various-lawyerings/01-aviary-classification/index.ts +++ b/projects/objects/various-lawyerings/01-aviary-classification/index.ts @@ -1,4 +1,10 @@ -// Write your Bird type here! ✨ +export type Bird = { + dangerous?: boolean; + diet: "carnivore" | "omnivore"; + intelligent?: boolean; + name: string; + noisy?: boolean; +}; export const birds: Bird[] = [ { diff --git a/projects/objects/various-lawyerings/02-case-management/index.ts b/projects/objects/various-lawyerings/02-case-management/index.ts index a6850141..f7a5f786 100644 --- a/projects/objects/various-lawyerings/02-case-management/index.ts +++ b/projects/objects/various-lawyerings/02-case-management/index.ts @@ -11,27 +11,24 @@ export const cases: Case[] = [ { court: "federal", decided: new Date("February 18, 1986"), - defendant: ["Glynn Batson", "and", "Southplains Land Corporation"], - id: 841710, + defendant: "Glynn Batson and Southplains Land Corporation", + id: "84-1710", plaintiff: "United States of America", title: "United States v. Batson", }, { court: "state", - decided: "April 17, 1992", + decided: new Date("April 17, 1992"), defendant: "Bradford Marine, Inc", id: ["90-6372-CIV", "90-6599-CIV"], plaintiff: "Lyn C. Noble", title: "Noble v. Bradford Marine, Inc", }, { - amusing: true, - court: "NY state", + court: "state", defendant: "PepsiCo, Inc.", - decided: { - on: new Date("August 5, 1999"), - }, - ids: ["96-cv-5320", "96-cv-9069"], + decided: new Date("August 5, 1999"), + id: ["96-cv-5320", "96-cv-9069"], plaintiff: "John Leonard", title: "Leonard v. Pepsico, Inc.", }, diff --git a/projects/objects/various-lawyerings/03-making-arguments/index.ts b/projects/objects/various-lawyerings/03-making-arguments/index.ts index df999946..aed1c68c 100644 --- a/projects/objects/various-lawyerings/03-making-arguments/index.ts +++ b/projects/objects/various-lawyerings/03-making-arguments/index.ts @@ -1,4 +1,37 @@ -// Write your types here! ✨ +export type MotionBase = { + from: "defendant" | "plaintiff"; + reason: string; +}; + +export type PreTrialMotion = MotionBase & { + classification: "dismiss" | "suppress" | "venue"; + step: "pre-trial"; +}; + +export type PostTrialMotion = MotionBase & { + classification: "acquittal" | "correction" | "new trial"; + step: "post-trial"; +}; + +export type TrialMotion = PostTrialMotion | PreTrialMotion; + +export type AllowedMotion = TrialMotion & { + deliberationHours: number; + status: "allowed"; +}; + +export type DeniedMotion = TrialMotion & { + annoyedJustice: boolean; + deliberationHours: number; + status: "denied"; +}; + +export type PendingMotion = TrialMotion & { + estimatedDeliberationHours: number; + status: "pending"; +}; + +export type Motion = AllowedMotion | DeniedMotion | PendingMotion; export const motions: Motion[] = [ { @@ -6,7 +39,8 @@ export const motions: Motion[] = [ classification: "acquittal", deliberationHours: 1, from: "defendant", - reason: "The heretofore document had dried ink on it.", + reason: + "I left all my money to Bruce Mathis, the real father of my children.", status: "denied", step: "post-trial", }, diff --git a/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/index.ts b/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/index.ts index 2665230b..46ea41c6 100644 --- a/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/index.ts +++ b/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/index.ts @@ -1,5 +1,5 @@ -namespace TheBard { - export function verifyProse(prose: string[]) { - return Poetry.schemesInOrder.find((scheme) => scheme.verify(prose))?.name; - } +import { schemesInOrder } from "./poetry.solution"; + +export function verifyProse(prose: string[]) { + return schemesInOrder.find((scheme) => scheme.verify(prose))?.name; } diff --git a/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/poetry.ts b/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/poetry.ts index c5275e74..8caaa47e 100644 --- a/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/poetry.ts +++ b/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/poetry.ts @@ -1,62 +1,56 @@ -namespace TheBard.Poetry { - namespace Schemes { - export const aabbaa = { - name: "AABBAA", - verify: (prose: string[]) => { - if (prose.length % 6 !== 0) { - return false; - } - - for (let i = 0; i < prose.length; i += 6) { - if ( - !Rhymes.doesRhyme(prose[i], prose[i + 1]) || - !Rhymes.doesRhyme(prose[i + 2], prose[i + 3]) || - !Rhymes.doesRhyme(prose[i + 4], prose[i + 5]) - ) { - return false; - } - } - - return true; - }, - }; - - export const abab = { - name: "ABAB", - verify: (prose: string[]) => { - if (prose.length % 4 !== 0) { - return false; - } - - for (let i = 0; i < prose.length; i += 6) { - if ( - !Rhymes.doesRhyme(prose[i], prose[i + 2]) || - !Rhymes.doesRhyme(prose[i + 1], prose[i + 3]) - ) { - return false; - } - } - - return true; - }, - }; - - export const limerick = { - name: "Limerick", - verify: (prose: string[]) => { - return ( - prose.length === 5 && - Rhymes.doesRhyme(prose[0], prose[1]) && - Rhymes.doesRhyme(prose[2], prose[3]) && - Rhymes.doesRhyme(prose[1], prose[4]) - ); - }, - }; - } - - export const schemesInOrder = [ - Schemes.aabbaa, - Schemes.abab, - Schemes.limerick, - ]; -} +import { doesRhyme } from "./rhymes.solution"; + +const aabbaa = { + name: "AABBAA", + verify: (prose: string[]) => { + if (prose.length % 6 !== 0) { + return false; + } + + for (let i = 0; i < prose.length; i += 6) { + if ( + !doesRhyme(prose[i], prose[i + 1]) || + !doesRhyme(prose[i + 2], prose[i + 3]) || + !doesRhyme(prose[i + 4], prose[i + 5]) + ) { + return false; + } + } + + return true; + }, +}; + +const abab = { + name: "ABAB", + verify: (prose: string[]) => { + if (prose.length % 4 !== 0) { + return false; + } + + for (let i = 0; i < prose.length; i += 6) { + if ( + !doesRhyme(prose[i], prose[i + 2]) || + !doesRhyme(prose[i + 1], prose[i + 3]) + ) { + return false; + } + } + + return true; + }, +}; + +const limerick = { + name: "Limerick", + verify: (prose: string[]) => { + return ( + prose.length === 5 && + doesRhyme(prose[0], prose[1]) && + doesRhyme(prose[2], prose[3]) && + doesRhyme(prose[1], prose[4]) + ); + }, +}; + +export const schemesInOrder = [aabbaa, abab, limerick]; diff --git a/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/rhymes.ts b/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/rhymes.ts index ba328677..d9272acf 100644 --- a/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/rhymes.ts +++ b/projects/syntax-extensions/typespeare/01-whats-in-a-namespace/rhymes.ts @@ -1,22 +1,20 @@ -namespace TheBard.Rhymes { - const vowels = new Set(["a", "e", "i", "o", "u", "y"]); +const vowels = new Set(["a", "e", "i", "o", "u", "y"]); - /** - * @remarks Very rudimentary rhyming detection. We should revisit. - */ - export function doesRhyme(a: string, b: string) { - const lastConsonantsA = getLastConsonants(a); +/** + * @remarks Very rudimentary rhyming detection. We should revisit. + */ +export function doesRhyme(a: string, b: string) { + const lastConsonantsA = getLastConsonants(a); - return lastConsonantsA && lastConsonantsA === getLastConsonants(b); - } + return lastConsonantsA && lastConsonantsA === getLastConsonants(b); +} - function getLastConsonants(text: string) { - for (let i = text.length - 1; i >= 0; i--) { - if (vowels.has(text[i])) { - return text.slice(i); - } +function getLastConsonants(text: string) { + for (let i = text.length - 1; i >= 0; i--) { + if (vowels.has(text[i])) { + return text.slice(i); } - - return text; } + + return text; } diff --git a/projects/syntax-extensions/typespeare/02-much-ado-about-enums/colors.ts b/projects/syntax-extensions/typespeare/02-much-ado-about-enums/colors.ts index a07d3de3..371a91bb 100644 --- a/projects/syntax-extensions/typespeare/02-much-ado-about-enums/colors.ts +++ b/projects/syntax-extensions/typespeare/02-much-ado-about-enums/colors.ts @@ -1,7 +1,5 @@ -export const Colors = { - Blue: "blue", - Red: "red", - Yellow: "yellow", -} as const; - -export type Color = typeof Colors[keyof typeof Colors]; +export enum Color { + Blue = "blue", + Red = "red", + Yellow = "yellow", +} diff --git a/projects/syntax-extensions/typespeare/02-much-ado-about-enums/index.ts b/projects/syntax-extensions/typespeare/02-much-ado-about-enums/index.ts index 398694b5..8fef7c1b 100644 --- a/projects/syntax-extensions/typespeare/02-much-ado-about-enums/index.ts +++ b/projects/syntax-extensions/typespeare/02-much-ado-about-enums/index.ts @@ -1,17 +1,16 @@ -import { Colors } from "./colors"; -import { rhymes } from "./rhymes"; +import { Color } from "./colors.solution"; +import { rhymes } from "./rhymes.solution"; -export function makeRhyme(color: keyof typeof Colors) { - const value = Colors[color]; - const [third, fourth] = rhymes[value]; +export function makeRhyme(color: Color) { + const [third, fourth] = rhymes[color]; console.log(`Roses are red,`); - console.log(`\tviolets are ${value}.`); + console.log(`\tviolets are ${color}.`); console.log(`${third},`); console.log(`\t${fourth}.`); console.log(""); } -makeRhyme("Blue"); -makeRhyme("Red"); -makeRhyme("Yellow"); +makeRhyme(Color.Blue); +makeRhyme(Color.Red); +makeRhyme(Color.Yellow); diff --git a/projects/syntax-extensions/typespeare/02-much-ado-about-enums/rhymes.ts b/projects/syntax-extensions/typespeare/02-much-ado-about-enums/rhymes.ts index f433ba42..4665e3fb 100644 --- a/projects/syntax-extensions/typespeare/02-much-ado-about-enums/rhymes.ts +++ b/projects/syntax-extensions/typespeare/02-much-ado-about-enums/rhymes.ts @@ -1,7 +1,7 @@ -import { Colors } from "./colors"; +import { Color } from "./colors.solution"; export const rhymes = { - [Colors.Blue]: ["Sugar is sweet", "and so are you"], - [Colors.Red]: ["If I'm a bagel", "you're the spread"], - [Colors.Yellow]: ["Whenever I'm stressed", "you make me mellow"], + [Color.Blue]: ["Sugar is sweet", "and so are you"], + [Color.Red]: ["If I'm a bagel", "you're the spread"], + [Color.Yellow]: ["Whenever I'm stressed", "you make me mellow"], }; diff --git a/projects/syntax-extensions/typespeare/03-typus-andronicus/conflict.ts b/projects/syntax-extensions/typespeare/03-typus-andronicus/conflict.ts index 26c2b725..cff4c7a4 100644 --- a/projects/syntax-extensions/typespeare/03-typus-andronicus/conflict.ts +++ b/projects/syntax-extensions/typespeare/03-typus-andronicus/conflict.ts @@ -1,4 +1,4 @@ -import { Setup } from "./setup"; +import type { Setup } from "./setup"; export type ConflictSeverity = "mild" | "moderate" | "severe"; diff --git a/projects/syntax-extensions/typespeare/03-typus-andronicus/index.ts b/projects/syntax-extensions/typespeare/03-typus-andronicus/index.ts index 0a49f918..1b981627 100644 --- a/projects/syntax-extensions/typespeare/03-typus-andronicus/index.ts +++ b/projects/syntax-extensions/typespeare/03-typus-andronicus/index.ts @@ -1,10 +1,14 @@ -import { Conflict, ConflictCreator, conflictCreators } from "./conflict"; import { - Resolution, - ResolutionCreator, + type Conflict, + type ConflictCreator, + conflictCreators, +} from "./conflict"; +import { + type Resolution, + type ResolutionCreator, resolutionCreators, } from "./resolution"; -import { Setup, SetupCreator, setupCreators } from "./setup"; +import { type Setup, type SetupCreator, setupCreators } from "./setup"; function getRandom(items: T[]) { return items[Math.floor(Math.random() * items.length)]; diff --git a/projects/syntax-extensions/typespeare/03-typus-andronicus/resolution.ts b/projects/syntax-extensions/typespeare/03-typus-andronicus/resolution.ts index aea91ede..11eed7fd 100644 --- a/projects/syntax-extensions/typespeare/03-typus-andronicus/resolution.ts +++ b/projects/syntax-extensions/typespeare/03-typus-andronicus/resolution.ts @@ -1,5 +1,5 @@ -import { Conflict } from "./conflict"; -import { Setup } from "./setup"; +import type { Conflict } from "./conflict"; +import type { Setup } from "./setup"; export interface Resolution { description: string; diff --git a/projects/the-type-system/system-of-a-clown/01-clowning-around/index.ts b/projects/the-type-system/system-of-a-clown/01-clowning-around/index.ts index 53c1d6ca..c471da45 100644 --- a/projects/the-type-system/system-of-a-clown/01-clowning-around/index.ts +++ b/projects/the-type-system/system-of-a-clown/01-clowning-around/index.ts @@ -3,9 +3,9 @@ let remainingGuests = 20; while (true) { // Each group of guests will be size 5-10 const guestsToAssign = Math.floor(Math.random() * 5) + 5; - let activity; // Those were some nice type annotations you had here! - let capacity; // It'd be a shame if we ... *erased* them! 😈 - let requiresSupplies; + let activity: string; + let capacity: number; + let requiresSupplies: boolean; switch (Math.floor(Math.random() * 5)) { case 0: @@ -15,7 +15,7 @@ while (true) { break; case 1: activity = "face painting"; - capacity = "1"; + capacity = 1; requiresSupplies = true; break; case 2: diff --git a/projects/the-type-system/system-of-a-clown/02-clown-availability/index.ts b/projects/the-type-system/system-of-a-clown/02-clown-availability/index.ts index 4029d12c..0e705380 100644 --- a/projects/the-type-system/system-of-a-clown/02-clown-availability/index.ts +++ b/projects/the-type-system/system-of-a-clown/02-clown-availability/index.ts @@ -1,13 +1,13 @@ // Note: I'm planning on inviting 20 guests in total. // Some clowns can only handle a certain number of guests. -let guestCount: boolean = 20; -let clownsCount = "zilch!"; +let guestCount = 20; +let clownsCount = 0; let krustyAvailability = true; let ronaldAvailability = true; let pennywiseAvailability = true; -let matchingsDescription: any = ""; +let matchingsDescription = ""; let lastClown; do { @@ -33,11 +33,11 @@ do { continue; } - // Pennywise asks: what's a sewer's favorite data type? + // Pennywise asks: What's a sewer's favorite data type? // Pennywise answers: Floats! if (pennywiseAvailability) { pennywiseAvailability = false; - matchingsDescription += "Pennywise w̺̞̠i̢͇͙l͇̞l͇͍̘ c͓͕̝o̡̠̞n̼̝s̡̞͎u͉̝͔m͚̪̞e̢͚̝ y̴̡̡͕͌̿́ó̸̢͇͚̾̕u̸̡̡͎͒͛r̸͕͓͖̈́͆͒ s̵̺̘̪͒͆̓o̵̡͚̟̽͆̚u̵̠͖̓͐͝l̸͓̘͇̐̓̚s̸̺͎̽̈́͆."; + matchingsDescription += "Pennywise w̺̞̠i̢͇͙l͇̞l͇͍̘ c͓͕̝o̡̠̞n̼̝s̡̞͎u͉̝͔m͚̪̞e̢͚̝ y̴̡̡͕͌̿́ó̸̢͇͚̾̕u̸̡̡͎͒͛."; lastClown = "Pennywise"; continue; } @@ -49,9 +49,9 @@ if (clownsCount > 2) { console.log("We've got a lot of clowns coming!"); } -if (matchingsDescription.length()) { +if (matchingsDescription.length) { console.log(`There will be ${clownsCount} clowns!\n`); - clownsole.log(matchingsDescription); + console.log(matchingsDescription); // Haha! console.log(`The last clown is: ${lastClown.toUpperCase()}!`); } else { console.log("Nobody gets a clown. Terrible party. Goodbye."); diff --git a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts index 95e0eab3..69d0aa2e 100644 --- a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts +++ b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { announceCharacter } = process.env.TEST_SOLUTIONS ? solution : index; +const { announceCharacter } = process.env.TEST_SOLUTIONS ? index : index; const mockLog = (console.log = jest.fn()); diff --git a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.ts b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.ts index dbb0f6cf..21a669fa 100644 --- a/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.ts +++ b/projects/type-modifiers/modifiers-of-the-types/01-fabulous-secret-powers/index.ts @@ -1,4 +1,15 @@ -import { characters } from "./characters"; +export interface Character { + name: string; + powers: string[]; + side: "good" | "evil"; +} -// Write your announceCharacter function here! ✨ -// You'll need to export it so the tests can run it. +export function announceCharacter(raw: string) { + const character = JSON.parse(raw) as Character; + + console.log(`I am ${character.name}.`); + console.log(`My powers are: ${character.powers.join(", ")}.`); + console.log(`I am ${character.side}.`); + + return character; +} diff --git a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts index 80acb065..3f4b6608 100644 --- a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts +++ b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { getArtifactType } = process.env.TEST_SOLUTIONS ? solution : index; +const { getArtifactType } = process.env.TEST_SOLUTIONS ? index : index; describe(getArtifactType, () => { describe("type", () => { diff --git a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.ts b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.ts index e1f4af86..e72b938d 100644 --- a/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.ts +++ b/projects/type-modifiers/modifiers-of-the-types/02-artifact-assertions/index.ts @@ -1,6 +1,3 @@ -// Write your types and function below! ✨ -// You'll need to export them so the tests can run it. - const artifacts = { "Black Garnet": { type: "magic", @@ -20,4 +17,10 @@ const artifacts = { "Sword of the Ancients": { type: "sword", }, -}; +} as const; + +export type ArtifactName = keyof typeof artifacts; + +export function getArtifactType(name: ArtifactName) { + return artifacts[name].type; +} diff --git a/projects/type-modifiers/type-force/src/index.test.ts b/projects/type-modifiers/type-force/src/index.test.ts index 7267cd67..b2bab081 100644 --- a/projects/type-modifiers/type-force/src/index.test.ts +++ b/projects/type-modifiers/type-force/src/index.test.ts @@ -4,7 +4,7 @@ import { expectType } from "tsd"; import * as index from "./index"; import * as solution from "./solution"; -const { duel } = process.env.TEST_SOLUTIONS ? solution : index; +const { duel } = process.env.TEST_SOLUTIONS ? index : index; describe(duel, () => { describe("types", () => { diff --git a/projects/type-modifiers/type-force/src/index.ts b/projects/type-modifiers/type-force/src/index.ts index 74a5188d..ba362cac 100644 --- a/projects/type-modifiers/type-force/src/index.ts +++ b/projects/type-modifiers/type-force/src/index.ts @@ -1,32 +1,38 @@ -// Write your duel function and types below! ✨ -// You'll need to export duel so the tests can run it. +export interface Character { + flying: boolean; + name: string; + power: number; + toughness: number; +} const mutationsLibrary = { - energy: (hero) => { + energy: (hero: Character) => { hero.power *= 1.25; hero.flying = true; }, - healing: (hero) => { + healing: (hero: Character) => { hero.toughness *= 2; }, - luck: (hero) => { + luck: (hero: Character) => { hero.power *= 1.25; hero.toughness *= 1.25; }, - flight: (hero) => { + flight: (hero: Character) => { hero.flying = true; }, - strength: (hero) => { + strength: (hero: Character) => { hero.power *= 2; }, - wings: (hero) => { + wings: (hero: Character) => { hero.flying = true; hero.toughness *= 0.9; }, -}; +} as const; + +export type Mutation = keyof typeof mutationsLibrary; -function createCharacter(name, mutations) { - const character = { +function createCharacter(name: string, mutations: Mutation[]) { + const character: Character = { flying: false, name, power: 1, @@ -39,3 +45,17 @@ function createCharacter(name, mutations) { return character; } + +export interface Fighter { + mutations: Mutation[]; + name: string; +} + +export function duel(good: Fighter, bad: Fighter) { + const hero = createCharacter(good.name, good.mutations); + const villain = createCharacter(bad.name, bad.mutations); + + return hero.power / villain.toughness >= villain.power / hero.toughness + ? (["hero", hero] as const) + : (["villain", villain] as const); +} diff --git a/projects/type-operations/array-type-shenanigans/01-flat-filter/index.ts b/projects/type-operations/array-type-shenanigans/01-flat-filter/index.ts index 9d996998..12a19233 100644 --- a/projects/type-operations/array-type-shenanigans/01-flat-filter/index.ts +++ b/projects/type-operations/array-type-shenanigans/01-flat-filter/index.ts @@ -1,2 +1,7 @@ -// Write your FilteredArrayItems type here! ✨ -// You'll need to export it so the tests can run it. +export type FilteredArrayItems = T extends (infer Item)[] + ? Item extends Filter + ? FilteredArrayItems + : never + : T extends Filter + ? T + : never; diff --git a/projects/type-operations/array-type-shenanigans/02-reverse/index.ts b/projects/type-operations/array-type-shenanigans/02-reverse/index.ts index 07dbcaba..121f6c64 100644 --- a/projects/type-operations/array-type-shenanigans/02-reverse/index.ts +++ b/projects/type-operations/array-type-shenanigans/02-reverse/index.ts @@ -1,2 +1,3 @@ -// Write your Reverse type here! ✨ -// You'll need to export it so the tests can run it. +export type Reverse = T extends [infer First, ...infer Rest] + ? [...Reverse, First] + : T; diff --git a/projects/type-operations/array-type-shenanigans/03-zip/index.ts b/projects/type-operations/array-type-shenanigans/03-zip/index.ts index 7dfc014c..c7a384ee 100644 --- a/projects/type-operations/array-type-shenanigans/03-zip/index.ts +++ b/projects/type-operations/array-type-shenanigans/03-zip/index.ts @@ -1,2 +1,8 @@ -// Write your Zip type here! ✨ -// You'll need to export it so the tests can run it. +export type Zip = T extends [ + infer FirstT, + ...infer RestT +] + ? U extends [infer FirstU, ...infer RestU] + ? [FirstT, FirstU, ...Zip] + : [...T] + : U; diff --git a/projects/type-operations/template-literal-type-shenanigans/01-spongecase/index.ts b/projects/type-operations/template-literal-type-shenanigans/01-spongecase/index.ts index 3738c208..b4611fb9 100644 --- a/projects/type-operations/template-literal-type-shenanigans/01-spongecase/index.ts +++ b/projects/type-operations/template-literal-type-shenanigans/01-spongecase/index.ts @@ -1,2 +1,8 @@ -// Write your SpOnGeCaSe type here! ✨ -// You'll need to export it so the tests can run it. +export type SpOnGeCaSe< + Text, + FirstTransform extends "upper" | "lower" = "upper" +> = Text extends `${infer First}${infer Rest}` + ? FirstTransform extends "upper" + ? `${Capitalize}${SpOnGeCaSe}` + : `${Lowercase}${SpOnGeCaSe}` + : ``; diff --git a/projects/type-operations/template-literal-type-shenanigans/02-word-replace/index.ts b/projects/type-operations/template-literal-type-shenanigans/02-word-replace/index.ts index 2e929ade..bab1e597 100644 --- a/projects/type-operations/template-literal-type-shenanigans/02-word-replace/index.ts +++ b/projects/type-operations/template-literal-type-shenanigans/02-word-replace/index.ts @@ -1,2 +1,7 @@ -// Write your WordReplace type here! ✨ -// You'll need to export it so the tests can run it. +export type WordReplace< + Text extends string, + Original extends string, + Replacement extends string +> = Text extends `${infer Prefix}${Original}${infer Suffix}` + ? WordReplace<`${Prefix}${Replacement}${Suffix}`, Original, Replacement> + : Text; diff --git a/projects/type-operations/template-literal-type-shenanigans/03-split-on/index.ts b/projects/type-operations/template-literal-type-shenanigans/03-split-on/index.ts index 66f0f72a..2fb49d91 100644 --- a/projects/type-operations/template-literal-type-shenanigans/03-split-on/index.ts +++ b/projects/type-operations/template-literal-type-shenanigans/03-split-on/index.ts @@ -1,2 +1,7 @@ -// Write your SplitOn type here! ✨ -// You'll need to export it so the tests can run it. +export type SplitOn< + Text extends string, + On extends string, + Results extends string[] = [] +> = Text extends `${infer Prefix}${On}${infer Suffix}` + ? SplitOn + : [Text, ...Results]; diff --git a/projects/unions-and-literals/primitive-cooking/01-ingredients/index.ts b/projects/unions-and-literals/primitive-cooking/01-ingredients/index.ts index 6f5599d1..db7743e3 100644 --- a/projects/unions-and-literals/primitive-cooking/01-ingredients/index.ts +++ b/projects/unions-and-literals/primitive-cooking/01-ingredients/index.ts @@ -1,8 +1,8 @@ // Please correct any type annotation problems here! ✨ -let arugula: number; +let arugula: number | undefined; let dressing: string; -let lettuce: number; -let mealDate: string; +let lettuce: number | undefined; +let mealDate: Date; arugula = 2; dressing = "honey dijon"; diff --git a/projects/unions-and-literals/primitive-cooking/02-recipes/index.ts b/projects/unions-and-literals/primitive-cooking/02-recipes/index.ts index 14786a7a..0d0bae53 100644 --- a/projects/unions-and-literals/primitive-cooking/02-recipes/index.ts +++ b/projects/unions-and-literals/primitive-cooking/02-recipes/index.ts @@ -1,6 +1,6 @@ // Please clarify any overly wide (permissive) type annotations here! ✨ -let difficulty: number; -let group: string; +let difficulty: 1 | 2 | 3; +let group: "appetizer" | "entree" | "dessert"; let title: string; // Start with something quick and painless to prepare... @@ -23,7 +23,7 @@ console.log(`[${group}] ${title}: ${difficulty}/3 difficulty`); // Send everyone off with a nice closer. difficulty = 1; -group = "desert"; +group = "dessert"; title = "Biscuits and Coffee"; console.log(`[${group}] ${title}: ${difficulty}/3 difficulty`); diff --git a/projects/unions-and-literals/primitive-cooking/03-seating/index.ts b/projects/unions-and-literals/primitive-cooking/03-seating/index.ts index c6db2c78..fb83a2fa 100644 --- a/projects/unions-and-literals/primitive-cooking/03-seating/index.ts +++ b/projects/unions-and-literals/primitive-cooking/03-seating/index.ts @@ -1,9 +1,8 @@ -// Please fill in any missing type annotations here... const headOfTable = "Me!"; -let adjacentLeft; -let adjacentRight; -let furtherLeft; -let furtherRight; +let adjacentLeft: "Susie" | "Tommy"; +let adjacentRight: "Susie" | "Tommy"; +let furtherLeft: "Angelica" | "Chuckie" | undefined; +let furtherRight: "Chuckie" | "Kimi" | "Timmy" | undefined; // I always invite Susie and Tommy! ♥ if (Math.random() > 0.5) { @@ -45,7 +44,7 @@ console.log(`At the head of the table is... ${headOfTable}`); console.log(`Adjacent to the left is: ${adjacentLeft}`); console.log(`Adjacent to the right is: ${adjacentRight}`); -console.log(`Further down on the left is: ${adjacentLeft ?? "nobody"}`); -console.log(`Further down on the right is: ${adjacentRight ?? "nobody"}`); +console.log(`Further down on the left is: ${furtherLeft ?? "nobody"}`); +console.log(`Further down on the right is: ${furtherRight ?? "nobody"}`); export {}; diff --git a/projects/unions-and-literals/the-narrow-trail/src/index.test.ts b/projects/unions-and-literals/the-narrow-trail/src/index.test.ts index 59265c22..a9b6c07e 100644 --- a/projects/unions-and-literals/the-narrow-trail/src/index.test.ts +++ b/projects/unions-and-literals/the-narrow-trail/src/index.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, jest, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./solution"; -const { runCommands } = process.env.TEST_SOLUTIONS ? solution : index; +const { runCommands } = process.env.TEST_SOLUTIONS ? index : index; const mockRandom = jest.spyOn(Math, "random"); diff --git a/projects/unions-and-literals/the-narrow-trail/src/index.ts b/projects/unions-and-literals/the-narrow-trail/src/index.ts index f41efbea..530cbc99 100644 --- a/projects/unions-and-literals/the-narrow-trail/src/index.ts +++ b/projects/unions-and-literals/the-narrow-trail/src/index.ts @@ -1,3 +1,61 @@ export function runCommands() { - // Declare your variables and runtime logic here! ✨ + let nextSupply: "food" | "water" | undefined; + let food = 5; + let water = 5; + + for (let time = 1; time <= 7; time += 1) { + const randomNumber = Math.floor(Math.random() * 6) + 1; + let command: "finish" | "food" | "water" | number; + + switch (randomNumber) { + case 1: + command = "food"; + break; + + case 2: + command = "water"; + break; + + default: + command = randomNumber; + break; + } + + if (typeof command === "number") { + switch (nextSupply) { + case "food": + food += command; + nextSupply = undefined; + break; + + case "water": + water += command; + nextSupply = undefined; + break; + + default: + nextSupply = randomNumber % 2 === 0 ? "food" : "water"; + break; + } + } + + switch (command) { + case "food": + nextSupply = "food"; + break; + + case "water": + nextSupply = "water"; + break; + } + + food -= 1; + water -= 1; + + if (food === 0 || water === 0) { + return false; + } + } + + return true; } diff --git a/projects/using-ide-features/temporary-code/src/Person.ts b/projects/using-ide-features/temporary-code/src/Person.ts new file mode 100644 index 00000000..3ed972ab --- /dev/null +++ b/projects/using-ide-features/temporary-code/src/Person.ts @@ -0,0 +1,5 @@ +export interface Person { + name: string; + quote: string; + type: string; +} diff --git a/projects/using-ide-features/temporary-code/src/criticizePerson.ts b/projects/using-ide-features/temporary-code/src/criticizePerson.ts new file mode 100644 index 00000000..6b9ef89b --- /dev/null +++ b/projects/using-ide-features/temporary-code/src/criticizePerson.ts @@ -0,0 +1,7 @@ +import { Person } from "./Person.solution"; + +export function criticizePerson(person: Person) { + if (person.quote.indexOf("#") !== -1) { + console.log(`\t${person.name} should stop going on Twitter so much...`); + } +} diff --git a/projects/using-ide-features/temporary-code/src/index.test.ts b/projects/using-ide-features/temporary-code/src/index.test.ts index 0c8e4297..c809dc44 100644 --- a/projects/using-ide-features/temporary-code/src/index.test.ts +++ b/projects/using-ide-features/temporary-code/src/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals"; import * as index from "./index"; import * as solution from "./index.solution"; -const { announceExamplePeople } = process.env.TEST_SOLUTIONS ? solution : index; +const { announceExamplePeople } = process.env.TEST_SOLUTIONS ? index : index; describe(announceExamplePeople, () => { it("does not include its own list of persons", () => { diff --git a/projects/using-ide-features/temporary-code/src/index.ts b/projects/using-ide-features/temporary-code/src/index.ts index 09c3c54c..bd21d8d8 100644 --- a/projects/using-ide-features/temporary-code/src/index.ts +++ b/projects/using-ide-features/temporary-code/src/index.ts @@ -1,27 +1,28 @@ +import { criticizePerson } from "./criticizePerson.solution"; +import { Person } from "./Person.solution"; + // Refactor here! ✨ -function tempCreatePersonTodoRefactorSoon(name: string, quote: string) { +function createPerson(name: string, quote: string): Person { return { name, quote, type: "person" }; } const examplePeople = [ - tempCreatePersonTodoRefactorSoon("archeologist", "insert archeologist quote"), - tempCreatePersonTodoRefactorSoon("historian", "insert historian quote"), - tempCreatePersonTodoRefactorSoon("programmer", "#shipit"), + createPerson("archeologist", "insert archeologist quote"), + createPerson("historian", "insert historian quote"), + createPerson("programmer", "#shipit"), ]; -function describePerson(person) { +function describePerson(person: Person) { const { name, type, quote } = person; - return "The " + name + " " + type + " goes: " + quote + "!"; + return `The ${name} ${type} goes: ${quote}!`; } export function announceExamplePeople() { for (const person of examplePeople) { console.log(describePerson(person)); - if (person.quote.indexOf("#") !== -1) { - console.log(`\t${person.name} should stop going on Twitter so much...`); - } + criticizePerson(person); } } diff --git a/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts b/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts index b6710490..3de27e2e 100644 --- a/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts +++ b/projects/using-ide-features/typearium/01-favorite-animals/index.test.ts @@ -4,7 +4,7 @@ import * as index from "./index"; import * as solution from "./solution"; const { checkIsAnyAnimalFavorite, getFavoriteAnimals, logFavoriteAnimals } = - process.env.TEST_SOLUTIONS ? solution : index; + process.env.TEST_SOLUTIONS ? index : index; describe(checkIsAnyAnimalFavorite, () => { it("returns true for a favorite animal", () => { diff --git a/projects/using-ide-features/typearium/01-favorite-animals/index.ts b/projects/using-ide-features/typearium/01-favorite-animals/index.ts index e5beb06a..d15470e6 100644 --- a/projects/using-ide-features/typearium/01-favorite-animals/index.ts +++ b/projects/using-ide-features/typearium/01-favorite-animals/index.ts @@ -1,35 +1,27 @@ // Refactor here! ✨ +const favoriteAnimals = [ + "parakeet", + "macaw", + "cat", + "monkey", + "elephant", + "alpaca", + "fox", +]; + export function checkIsAnyAnimalFavorite(...animals: string[]) { - const favoriteAnimalsUnique = new Set([ - "parakeet", - "macaw", - "cat", - "monkey", - "elephant", - "alpaca", - "fox", - ]); + const favoriteAnimalsUnique = new Set(favoriteAnimals); return animals.some((animal) => favoriteAnimalsUnique.has(animal)); } -export function getFavoriteAnimals(max = Infinity) { - return [ - "parakeet", - "macaw", - "cat", - "monkey", - "elephant", - "alpaca", - "fox", - ].slice(0, max); +export function getFavoriteAnimals(quantity = Infinity) { + return favoriteAnimals.slice(0, quantity); } export function logFavoriteAnimals() { - ["parakeet", "macaw", "cat", "monkey", "elephant", "alpaca", "fox"].forEach( - (animal, i) => { - console.log(`I like ${animal} number ${i}!`); - } - ); + favoriteAnimals.forEach((animal, i) => { + console.log(`I like ${animal} number ${i}!`); + }); } diff --git a/projects/using-ide-features/typearium/02-species-collections/fauna.ts b/projects/using-ide-features/typearium/02-species-collections/fauna.ts new file mode 100644 index 00000000..91560219 --- /dev/null +++ b/projects/using-ide-features/typearium/02-species-collections/fauna.ts @@ -0,0 +1,11 @@ +import { getMammals, MammalsSettings } from "./fauna/mammals.solution"; +import { getReptiles, ReptilesSettings } from "./fauna/reptiles.solution"; + +export interface FaunaSettings { + mammals?: MammalsSettings; + reptiles?: ReptilesSettings; +} + +export function getFauna(settings?: FaunaSettings) { + return [getMammals(settings?.mammals), getReptiles(settings?.reptiles)]; +} diff --git a/projects/using-ide-features/typearium/02-species-collections/fauna/mammals.ts b/projects/using-ide-features/typearium/02-species-collections/fauna/mammals.ts new file mode 100644 index 00000000..db22f5c3 --- /dev/null +++ b/projects/using-ide-features/typearium/02-species-collections/fauna/mammals.ts @@ -0,0 +1,17 @@ +import { onlyTruthy } from "../utils/onlyTruthy.solution"; + +export interface MammalsSettings { + cute?: boolean; + deadly?: boolean; +} + +export function getMammals(settings?: MammalsSettings) { + return onlyTruthy( + settings?.cute && [ + "cats", + "dogs", + settings?.deadly && "monty python rabbit", + ], + settings?.deadly && ["lion", "tiger"] + ); +} diff --git a/projects/using-ide-features/typearium/02-species-collections/fauna/reptiles.ts b/projects/using-ide-features/typearium/02-species-collections/fauna/reptiles.ts new file mode 100644 index 00000000..4c29c56b --- /dev/null +++ b/projects/using-ide-features/typearium/02-species-collections/fauna/reptiles.ts @@ -0,0 +1,13 @@ +import { onlyTruthy } from "../utils/onlyTruthy.solution"; + +export interface ReptilesSettings { + ferocious?: boolean; + small?: boolean; +} + +export function getReptiles(settings?: ReptilesSettings) { + return onlyTruthy( + settings?.ferocious && "dragon", + settings?.small && ["frog", "gecko"] + ); +} diff --git a/projects/using-ide-features/typearium/02-species-collections/flora.ts b/projects/using-ide-features/typearium/02-species-collections/flora.ts new file mode 100644 index 00000000..2dc403d8 --- /dev/null +++ b/projects/using-ide-features/typearium/02-species-collections/flora.ts @@ -0,0 +1,11 @@ +import { FlowersSettings, getFlowers } from "./flora/flowers.solution"; +import { TreesSettings, getTrees } from "./flora/trees.solution"; + +export interface FloraSettings { + flowers?: FlowersSettings; + trees?: TreesSettings; +} + +export function getFlora(settings?: FloraSettings) { + return [getFlowers(settings?.flowers), getTrees(settings?.trees)]; +} diff --git a/projects/using-ide-features/typearium/02-species-collections/flora/flowers.ts b/projects/using-ide-features/typearium/02-species-collections/flora/flowers.ts new file mode 100644 index 00000000..4f65a71e --- /dev/null +++ b/projects/using-ide-features/typearium/02-species-collections/flora/flowers.ts @@ -0,0 +1,13 @@ +import { onlyTruthy } from "../utils/onlyTruthy.solution"; + +export interface FlowersSettings { + colorful?: boolean; + prickly?: boolean; +} + +export function getFlowers(settings?: FlowersSettings) { + return onlyTruthy( + settings?.colorful && ["carnation", "lilac", "tulip"], + settings?.colorful && settings?.prickly && "rose" + ); +} diff --git a/projects/using-ide-features/typearium/02-species-collections/flora/trees.ts b/projects/using-ide-features/typearium/02-species-collections/flora/trees.ts new file mode 100644 index 00000000..26ca2967 --- /dev/null +++ b/projects/using-ide-features/typearium/02-species-collections/flora/trees.ts @@ -0,0 +1,13 @@ +import { onlyTruthy } from "../utils/onlyTruthy.solution"; + +export interface TreesSettings { + evergreen?: boolean; + fruitBearing?: boolean; +} + +export function getTrees(settings?: TreesSettings) { + return onlyTruthy( + settings?.evergreen && "pine", + settings?.fruitBearing && ["apple", "pear"] + ); +} diff --git a/projects/using-ide-features/typearium/02-species-collections/index.test.ts b/projects/using-ide-features/typearium/02-species-collections/index.test.ts index 59c14cce..0537cc6d 100644 --- a/projects/using-ide-features/typearium/02-species-collections/index.test.ts +++ b/projects/using-ide-features/typearium/02-species-collections/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, test } from "@jest/globals"; import * as index from "./index"; import * as solution from "./index.solution"; -const { getEverything } = process.env.TEST_SOLUTIONS ? solution : index; +const { getEverything } = process.env.TEST_SOLUTIONS ? index : index; describe(getEverything, () => { it("returns nothing with no settings", () => { diff --git a/projects/using-ide-features/typearium/02-species-collections/index.ts b/projects/using-ide-features/typearium/02-species-collections/index.ts index 00af396e..58f4137c 100644 --- a/projects/using-ide-features/typearium/02-species-collections/index.ts +++ b/projects/using-ide-features/typearium/02-species-collections/index.ts @@ -1,94 +1,8 @@ // Refactor here! ✨ -export type DeepStringsMaybe = - | string - | boolean - | undefined - | DeepStringsMaybe[]; - -function onlyTruthy(...items: DeepStringsMaybe[]): string[] { - return items.flatMap((item) => { - if (typeof item === "string") { - return item; - } - - if (item instanceof Array) { - return item.flatMap((subItem) => onlyTruthy(subItem)); - } - - return []; - }); -} - -interface MammalsSettings { - cute?: boolean; - deadly?: boolean; -} - -function getMammals(settings?: MammalsSettings) { - return onlyTruthy( - settings?.cute && [ - "cats", - "dogs", - settings?.deadly && "monty python rabbit", - ], - settings?.deadly && ["lion", "tiger"] - ); -} - -interface ReptilesSettings { - ferocious?: boolean; - small?: boolean; -} - -function getReptiles(settings?: ReptilesSettings) { - return onlyTruthy( - settings?.ferocious && "dragon", - settings?.small && ["frog", "gecko"] - ); -} - -interface FaunaSettings { - mammals?: MammalsSettings; - reptiles?: ReptilesSettings; -} - -function getFauna(settings?: FaunaSettings) { - return [getMammals(settings?.mammals), getReptiles(settings?.reptiles)]; -} - -interface FlowersSettings { - colorful?: boolean; - prickly?: boolean; -} - -function getFlowers(settings?: FlowersSettings) { - return onlyTruthy( - settings?.colorful && ["carnation", "lilac", "tulip"], - settings?.colorful && settings?.prickly && "rose" - ); -} - -interface TreesSettings { - evergreen?: boolean; - fruitBearing?: boolean; -} - -function getTrees(settings?: TreesSettings) { - return onlyTruthy( - settings?.evergreen && "pine", - settings?.fruitBearing && ["apple", "pear"] - ); -} - -interface FloraSettings { - flowers?: FlowersSettings; - trees?: TreesSettings; -} - -function getFlora(settings?: FloraSettings) { - return [getFlowers(settings?.flowers), getTrees(settings?.trees)]; -} +import { FaunaSettings, getFauna } from "./fauna.solution"; +import { FloraSettings, getFlora } from "./flora.solution"; +import { onlyTruthy } from "./utils/onlyTruthy.solution"; export interface EverythingSettings { fauna?: FaunaSettings; diff --git a/projects/using-ide-features/typearium/02-species-collections/utils/onlyTruthy.ts b/projects/using-ide-features/typearium/02-species-collections/utils/onlyTruthy.ts new file mode 100644 index 00000000..5a83288d --- /dev/null +++ b/projects/using-ide-features/typearium/02-species-collections/utils/onlyTruthy.ts @@ -0,0 +1,19 @@ +export type DeepStringsMaybe = + | string + | boolean + | undefined + | DeepStringsMaybe[]; + +export function onlyTruthy(...items: DeepStringsMaybe[]): string[] { + return items.flatMap((item) => { + if (typeof item === "string") { + return item; + } + + if (item instanceof Array) { + return item.flatMap((subItem) => onlyTruthy(subItem)); + } + + return []; + }); +} diff --git a/scripts/copy-solutions.mjs b/scripts/copy-solutions.mjs index a396ac6f..3e2551f3 100644 --- a/scripts/copy-solutions.mjs +++ b/scripts/copy-solutions.mjs @@ -16,7 +16,7 @@ for (const testFile of await glob("projects/**/*.test.*")) { } for (const solutionPath of await glob("projects/**/*solution*")) { - const replacedPath = solutionPath.includes("solution.") + const replacedPath = solutionPath.includes("/solution.") ? solutionPath.replace("solution", "index") : solutionPath.replace(".solution", "");