-
-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Type Modifiers > Prickly Predicates appetizer (#280)
- Loading branch information
1 parent
f0967f4
commit a981101
Showing
23 changed files
with
417 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
projects/type-modifiers/prickly-predicates/01-pruning-pests/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Step 1: Pruning Pests | ||
|
||
Thanks for signing on to the farm, friend! | ||
That's mighty kind of you. | ||
We sure do appreciate it. | ||
|
||
What we'll need from you first is help narrowing down the names of our fruits. | ||
We know what we grow, but these darn type systems don't. | ||
Can you help us out with a function to return whether a string is a known crop name? | ||
|
||
## Specification | ||
|
||
Export a type predicate function named `isCropName` that takes in a name of type `string`. | ||
It should return whether the data is one of the keys of the type of the existing `cropFamilies` object. | ||
|
||
## Files | ||
|
||
- `index.ts`: Write your `isCropName` function here | ||
- `index.test.ts`: Tests verifying `isCropName` | ||
- `solution.ts`: Solution code | ||
|
||
## Notes | ||
|
||
- The function's return type should be an explicit type predicate with the `is` keyword |
30 changes: 30 additions & 0 deletions
30
projects/type-modifiers/prickly-predicates/01-pruning-pests/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { describe, expect, it, test } from "@jest/globals"; | ||
import { expectType } from "tsd"; | ||
|
||
import * as index from "./index"; | ||
import * as solution from "./solution"; | ||
|
||
const { isCropName } = process.env.TEST_SOLUTIONS | ||
? solution | ||
: (index as typeof solution); | ||
|
||
describe(isCropName, () => { | ||
describe("types", () => { | ||
test("function type", () => { | ||
expectType<(name: string) => name is keyof typeof solution.cropFamilies>( | ||
isCropName | ||
); | ||
}); | ||
}); | ||
|
||
it.each([ | ||
["", false], | ||
["dandelion", false], | ||
["purslane", false], | ||
["cactus", true], | ||
["cassava", true], | ||
["chia", true], | ||
])("when given %j, returns %j", (input, expected) => { | ||
expect(isCropName(input)).toBe(expected); | ||
}); | ||
}); |
8 changes: 8 additions & 0 deletions
8
projects/type-modifiers/prickly-predicates/01-pruning-pests/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const cropFamilies = { | ||
cactus: "Cactaceae", | ||
cassava: "Euphorbiaceae", | ||
chia: "Lamiaceae", | ||
}; | ||
|
||
// Write your isCropName function here! ✨ | ||
// You'll need to export it so the tests can run it. |
9 changes: 9 additions & 0 deletions
9
projects/type-modifiers/prickly-predicates/01-pruning-pests/solution.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const cropFamilies = { | ||
cactus: "Cactaceae", | ||
cassava: "Euphorbiaceae", | ||
chia: "Lamiaceae", | ||
}; | ||
|
||
export function isCropName(name: string): name is keyof typeof cropFamilies { | ||
return name in cropFamilies; | ||
} |
4 changes: 4 additions & 0 deletions
4
projects/type-modifiers/prickly-predicates/01-pruning-pests/tsconfig.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"extends": "../../../../tsconfig.json", | ||
"include": ["."] | ||
} |
37 changes: 37 additions & 0 deletions
37
projects/type-modifiers/prickly-predicates/02-plant-particulars/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Step 2: Plant Particulars | ||
|
||
Well, I'll be darned! | ||
You blew through that first step faster than a dog chasing a roadrunner. | ||
Hee-yah! | ||
|
||
Our second request of you is to deal with is weeding. | ||
We're sick and tired of these invasive weeds in our dadgum farm! | ||
They're just about as welcome as a rattlesnake at a square dance. | ||
|
||
Can you help us write a function that filters data to just a known crop we want to grow? | ||
That'd be mighty useful in helping us skedaddle out those worrisome weeds. | ||
|
||
## Specification | ||
|
||
Export a type predicate function named `isAnyCrop` that takes in data of type `unknown`. | ||
It should return whether the data is an object that matches the existing `AnyCrop` interface. | ||
|
||
> Tip: when a value is type `object`, TypeScript won't allow you to access a property unless you check first that the property's key is `in` the value: | ||
> | ||
> ```ts | ||
> function checkValue(value: unknown) { | ||
> if (!!value && typeof value === "object" && "key" in value) { | ||
> console.log(value.key); | ||
> } | ||
> } | ||
> ``` | ||
## Files | ||
- `index.ts`: Write your `isAnyCrop` function here | ||
- `index.test.ts`: Tests verifying `isAnyCrop` | ||
- `solution.ts`: Solution code | ||
## Notes | ||
- The function's return type should be an explicit type predicate with the `is` keyword |
41 changes: 41 additions & 0 deletions
41
projects/type-modifiers/prickly-predicates/02-plant-particulars/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { describe, expect, it, test } from "@jest/globals"; | ||
import { expectType } from "tsd"; | ||
|
||
import * as index from "./index"; | ||
import * as solution from "./solution"; | ||
|
||
const { isAnyCrop } = process.env.TEST_SOLUTIONS | ||
? solution | ||
: (index as typeof solution); | ||
|
||
describe(isAnyCrop, () => { | ||
describe("types", () => { | ||
test("function type", () => { | ||
expectType<(data: solution.AnyCrop) => data is solution.AnyCrop>( | ||
isAnyCrop | ||
); | ||
}); | ||
}); | ||
|
||
it.each([ | ||
[null, false], | ||
[undefined, false], | ||
["", false], | ||
[123, false], | ||
[[], false], | ||
[{}, false], | ||
[{ growth: null }, false], | ||
[{ growth: 123 }, false], | ||
[{ harvested: true }, false], | ||
[{ name: "cactus" }, false], | ||
[{ growth: null, harvested: true, name: "cactus" }, false], | ||
[{ growth: 5, harvested: null, name: "cactus" }, false], | ||
[{ growth: 5, harvested: true, name: null }, false], | ||
[{ growth: 5, harvested: true, name: "other" }, false], | ||
[{ growth: 5, harvested: true, name: "cactus" }, true], | ||
[{ growth: 5, harvested: true, name: "cassava" }, true], | ||
[{ growth: 5, harvested: true, name: "chia" }, true], | ||
])("when given %j, returns %j", (input, expected) => { | ||
expect(isAnyCrop(input)).toBe(expected); | ||
}); | ||
}); |
8 changes: 8 additions & 0 deletions
8
projects/type-modifiers/prickly-predicates/02-plant-particulars/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export interface AnyCrop { | ||
growth: number; | ||
harvested: boolean; | ||
name: "cactus" | "cassava" | "chia"; | ||
} | ||
|
||
// Write your isAnyCrop function here! ✨ | ||
// You'll need to export it so the tests can run it. |
19 changes: 19 additions & 0 deletions
19
projects/type-modifiers/prickly-predicates/02-plant-particulars/solution.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export interface AnyCrop { | ||
growth: number; | ||
harvested: boolean; | ||
name: "cactus" | "cassava" | "chia"; | ||
} | ||
|
||
export function isAnyCrop(data: unknown): data is AnyCrop { | ||
return ( | ||
!!data && | ||
typeof data === "object" && | ||
"growth" in data && | ||
typeof data.growth === "number" && | ||
"harvested" in data && | ||
typeof data.harvested === "boolean" && | ||
"name" in data && | ||
typeof data.name === "string" && | ||
["cactus", "cassava", "chia"].includes(data.name) | ||
); | ||
} |
4 changes: 4 additions & 0 deletions
4
projects/type-modifiers/prickly-predicates/02-plant-particulars/tsconfig.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"extends": "../../../../tsconfig.json", | ||
"include": ["."] | ||
} |
28 changes: 28 additions & 0 deletions
28
projects/type-modifiers/prickly-predicates/03-picking-pears/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Step 3: Picking Pears | ||
|
||
Well, narrow my types and call me a structurally matched type. | ||
Aren't you just the most type-safe cowboy this side of the Sammamish River! | ||
|
||
Our next and final request of you is to deal with a juicy one. | ||
You're going to help us harvest some succulent cactus pears! | ||
They make a mighty fine jam, if I do say so myself. | ||
|
||
We can give you a whole array of potential cacti. | ||
We'll need you to return back all the cacti with fruits. | ||
|
||
## Specification | ||
|
||
Export two functions: | ||
|
||
- `isFruitBearingCactus`: a type predicate function named that takes in data of the provided `Cactus` interface and returns whether data is type `FruitBearingCactus` | ||
- `pickFruitBearingCacti`: a function that takes an array of `Cactus` objects and returns an array consisting of all the `FruitBearingCactus` elements | ||
|
||
## Files | ||
|
||
- `index.ts`: Write your `isFruitBearingCactus` and `pickFruitBearingCacti` functions here | ||
- `index.test.ts`: Tests verifying `isFruitBearingCactus` and `pickFruitBearingCacti` | ||
- `solution.ts`: Solution code | ||
|
||
## Notes | ||
|
||
- The function's return type should be an explicit type predicate with the `is` keyword |
82 changes: 82 additions & 0 deletions
82
projects/type-modifiers/prickly-predicates/03-picking-pears/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { describe, expect, it, test } from "@jest/globals"; | ||
import { expectType } from "tsd"; | ||
|
||
import * as index from "./index"; | ||
import * as solution from "./solution"; | ||
|
||
const { isFruitBearingCactus, pickFruitBearingCacti } = process.env | ||
.TEST_SOLUTIONS | ||
? solution | ||
: (index as typeof solution); | ||
|
||
describe(isFruitBearingCactus, () => { | ||
describe("types", () => { | ||
test("function type", () => { | ||
expectType< | ||
(data: solution.Cactus) => data is solution.FruitBearingCactus | ||
>(isFruitBearingCactus); | ||
}); | ||
}); | ||
|
||
it.each<[solution.Cactus, boolean]>([ | ||
[{ picked: false, state: "dormant" }, false], | ||
[{ picked: true, state: "dormant" }, false], | ||
[{ flowers: "small", state: "flowering" }, false], | ||
[{ flowers: "medium", state: "flowering" }, false], | ||
[{ flowers: "large", state: "flowering" }, false], | ||
[{ fruits: 0, state: "fruit-bearing" }, true], | ||
[{ fruits: 1, state: "fruit-bearing" }, true], | ||
[{ fruits: 2, state: "fruit-bearing" }, true], | ||
])("when given %j, returns %j", (input, expected) => { | ||
expect(isFruitBearingCactus(input)).toBe(expected); | ||
}); | ||
}); | ||
|
||
describe(pickFruitBearingCacti, () => { | ||
describe("types", () => { | ||
test("function type", () => { | ||
expectType<(data: solution.Cactus[]) => solution.FruitBearingCactus[]>( | ||
pickFruitBearingCacti | ||
); | ||
}); | ||
}); | ||
|
||
it.each<[solution.Cactus[], solution.Cactus[]]>([ | ||
[[], []], | ||
[[{ picked: true, state: "dormant" }], []], | ||
[[{ flowers: "small", state: "flowering" }], []], | ||
[[{ flowers: "medium", state: "flowering" }], []], | ||
[[{ flowers: "large", state: "flowering" }], []], | ||
[ | ||
[{ fruits: 0, state: "fruit-bearing" }], | ||
[{ fruits: 0, state: "fruit-bearing" }], | ||
], | ||
[ | ||
[{ fruits: 1, state: "fruit-bearing" }], | ||
[{ fruits: 1, state: "fruit-bearing" }], | ||
], | ||
[ | ||
[{ fruits: 2, state: "fruit-bearing" }], | ||
[{ fruits: 2, state: "fruit-bearing" }], | ||
], | ||
[ | ||
[ | ||
{ picked: true, state: "dormant" }, | ||
{ flowers: "small", state: "flowering" }, | ||
], | ||
[], | ||
], | ||
[ | ||
[ | ||
{ picked: true, state: "dormant" }, | ||
{ flowers: "small", state: "flowering" }, | ||
{ flowers: "medium", state: "flowering" }, | ||
{ flowers: "large", state: "flowering" }, | ||
{ fruits: 0, state: "fruit-bearing" }, | ||
], | ||
[{ fruits: 0, state: "fruit-bearing" }], | ||
], | ||
])("when given %j, returns %j", (input, expected) => { | ||
expect(pickFruitBearingCacti(input)).toEqual(expected); | ||
}); | ||
}); |
19 changes: 19 additions & 0 deletions
19
projects/type-modifiers/prickly-predicates/03-picking-pears/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export type Cactus = DefaultCactus | FloweringCactus | FruitBearingCactus; | ||
|
||
export interface FloweringCactus { | ||
flowers: "small" | "medium" | "large"; | ||
state: "flowering"; | ||
} | ||
|
||
export interface FruitBearingCactus { | ||
fruits: number; | ||
state: "fruit-bearing"; | ||
} | ||
|
||
export interface DefaultCactus { | ||
picked: boolean; | ||
state: "default"; | ||
} | ||
|
||
// Write your isFruitBearingCactus and pickFruitBearingCacti functions here! ✨ | ||
// You'll need to export it so the tests can run it. |
26 changes: 26 additions & 0 deletions
26
projects/type-modifiers/prickly-predicates/03-picking-pears/solution.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
export type Cactus = DormantCactus | FloweringCactus | FruitBearingCactus; | ||
|
||
export interface DormantCactus { | ||
picked: boolean; | ||
state: "dormant"; | ||
} | ||
|
||
export interface FloweringCactus { | ||
flowers: "small" | "medium" | "large"; | ||
state: "flowering"; | ||
} | ||
|
||
export interface FruitBearingCactus { | ||
fruits: number; | ||
state: "fruit-bearing"; | ||
} | ||
|
||
export function isFruitBearingCactus( | ||
cactus: Cactus | ||
): cactus is FruitBearingCactus { | ||
return cactus.state === "fruit-bearing"; | ||
} | ||
|
||
export function pickFruitBearingCacti(cacti: Cactus[]) { | ||
return cacti.filter(isFruitBearingCactus); | ||
} |
4 changes: 4 additions & 0 deletions
4
projects/type-modifiers/prickly-predicates/03-picking-pears/tsconfig.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"extends": "../../../../tsconfig.json", | ||
"include": ["."] | ||
} |
Oops, something went wrong.