Skip to content

Instantly share code, notes, and snippets.

@danielgek
Created January 11, 2022 14:50
Show Gist options
  • Save danielgek/abdfe9d0131ab1e19355ab5c19e5f833 to your computer and use it in GitHub Desktop.
Save danielgek/abdfe9d0131ab1e19355ab5c19e5f833 to your computer and use it in GitHub Desktop.
let hello = "a string";
hello = 5;
const shouldBeAString: string = true;
function addOne(x: number) {
return x + 1;
}
const four = addOne(3);
const notANumber = addOne("hello");
function timesThree(x) {
return x * 3;
}
timesThree("hello");
const timesFour = (x: number) => x * '4';
/*
* ======================================================
* TODO: Update Earthlings to allow these values:
* ======================================================*/
type Earthling = {};
let dog: Earthling = { type: "animal", name: "Fido" };
let cat: Earthling = { type: "animal", name: "Suki" };
let zucchini: Earthling = {
type: "vegetable",
name: "Zucchini"
};
let rose: Earthling = { type: "vegetable", name: "Rose" };
let quartz: Earthling = { type: "mineral", name: "Quartz" };
let diamond: Earthling = { type: "mineral", name: "Diamond" };
/* typings:expect-error */
const invalidEarthling: Earthling = { type: "martian", name: "Quux" }
/* typings:expect-error */
const invalidEarthling2: Earthling = { type: "plutonian" }
/*
* ======================================================
* TODO: Update Aliens to allow these values:
* ======================================================*/
type Alien = {};
let alienBlaxnor: Alien = {
name: "Blaxnor",
homePlanet: "Jupiter",
phaser: true
};
let alienXanter: Alien = {
name: "Xanter",
homePlanet: "Mars",
phaser: false
};
/*
* ======================================================
* TODO: Confirm that EarthlingOrAlien allows and disallows
* the following values
* ======================================================*/
type EarthlingOrAlien = Earthling | Alien;
let person: EarthlingOrAlien = { type: "animal", name: "Fernando" }
let alien: EarthlingOrAlien = { name: "Mac", homePlanet: "Pluto", phaser: true }
// typings:expect-error
let star: EarthlingOrAlien = "Sirius";
// typings:expect-error
let galaxy: EarthlingOrAlien = {
type: "LocalGalaxy",
name: "Milky Way"
};
// typings:expect-error
let asteroid: EarthlingOrAlien = {
homePlanet: false,
name: "Asteroid"
};
/**
* If we have a value of a union type, we can access common properties
*/
function printName(creature: EarthlingOrAlien){
console.log(creature.name)
}
/**
* But we cannot access properties that aren't common to all constituents
*/
function printType(creature: EarthlingOrAlien) {
// typings:expect-error
console.log(creature.type);
}
/*
* Intersections allow us to combine type definitions to
* create a single type with all the attributes of both types.
* This is useful when we have some set of properties that are
* shared among many different types. */
type Cat = {
animalType: "cat";
breedName: string;
coloration: "tabby" | "solid-colored" | "spotted";
};
type Dog = {
animalType: "dog";
breedName: string;
size: "teacup" | "toy" | "standard" | "huge";
};
type PetInfo = { name: string; familyName: string };
// We can define types that are both pet and Cat (or Dog) by intersecting
// the PetInfo type with the species type.
type PetCat = PetInfo & Cat;
type PetDog = PetInfo & Dog;
/*
* ======================================================
* TODO: Describe a valid PetCat and PetDog below.
* HINT: Use autocompletion (ctrl-space) to help you fill in the properties.
* ======================================================*/
const sukiTheCat: PetCat = {};
const finnTheDog: PetDog = {};
function announcePet(pet: PetInfo) {
return `This is ${pet.familyName} family pet, ${pet.name}.`;
}
/*
* Notice that announcePet will take any object that's
* structurally compatible with type Pet. */
announcePet(sukiTheCat);
announcePet(finnTheDog);
/* ======================================================
* TS understands control flow.
*
* TODO: Take a look at the type of 'fruit' each time it's referenced in
* isBanana below.
* ====================================================== */
function isBanana(fruit: 'banana' | 'apple' | 'pear') {
if (fruit === "banana") {
return `${fruit} is a banana`;
} else {
return ` ${fruit} is not a banana`;
}
}
/* ======================================================
* TODO: Check out the return type of classify. TS has figured out
* which specific strings can possibly be returned.
* ====================================================== */
function classify(n: number) {
if (n < 0) return "negative";
if (n > 0) return "positive";
return "zero";
}
/*
* TS checks our declared return type against the inferred type
* from all code paths in a function.
*
* This makes it easy to tell if we've handled all cases.
*
* ======================================================
* TODO: Try commenting out one of the return statements in
* describeNumber and see what happens.
* ======================================================*/
function describeNumber(num: number): string {
const value = classify(num);
if (value === "negative") {
return `${num} is a negative number`;
} else if (value === "positive") {
return `${num} is a positive number`;
} else {
return `${num} is zero`;
}
}
/*
* ======================================================
* TODO: Change ONLY the input type FruitColor so that the
* function below will only accept inputs it is able to classify
* ======================================================*/
type FruitColor = string;
function appleOrBanana(fruitColor: FruitColor): "apple" | "banana" {
switch (fruitColor) {
case "red":
return "apple";
case "yellow":
return "banana";
}
}
/*
* Let's take a look at the HotDrink type described in the slides.
*
* TS can narrow types as we move through control flow. When
* we're handling different cases of a union type, it's helpful
* to have a single property that is shared between all the cases,
* but has a different literal value for each case in the union. This allows
* us to use type narrowing to determine which case we're dealing with.
*
* This is great for when we have a bunch of similar objects with
* different constraints
*
* This technique - uninioning object types together with a shared, discriminating field -
* is called a "discriminated union"
*/
type Tea = {
type: "tea"; // Discriminant field
style: "green" | "black" | "herbal";
name: string;
};
type Coffee = {
type: "coffee"; // Discriminant field
roast: "dark" | "medium" | "light";
name: string;
};
type HotDrink = Tea | Coffee;
/**
* Since type is common to both Tea and Coffee, we can test it to
* figure out which we're dealing with.
*
* ======================================================
* TODO: Write a function that takes a HotDrink and
* returns the style, for tea, or the roast, for coffee.
* ======================================================
*/
function describe(
drink: HotDrink
): "green" | "black" | "herbal" | "dark" | "medium" | "light" {}
const rachaelsDrink: Tea = {name: "Chamomile", style: "herbal", type: "tea"}
const drewsDrink: Coffee = {name: "Onyx Columbia San Jose", roast: "light", type: "coffee"}
const herbalStyle = describe(rachaelsDrink);
const lightStyle = describe(drewsDrink);
/*
* ======================================================
* TODO: Define FruitType as a discriminated union so that
* the type proves that apples are red and can be polished,
* and bananas are yellow and can be peeled.
* ======================================================
*/
type FruitType = unknown;
// Hint: To define a function property within an object, write something like:
// type SomeObject = {
// myFunc: () => void
// };
function doSomething(fruit: FruitType) {
// FruitType must have a `type` field as a discriminant
switch (fruit.type) {
case "apple":
// Apples must be the color red in this example
const color: "red" = fruit.color
console.log(color);
// For this example, apples MUST NOT be yellow
// typings:expect-error
const wrongColor: "yellow" = fruit.color
console.log(wrongColor);
// Apples must have a polish function property
fruit.polish();
// Apples must not have a peel function property
// typings:expect-error
fruit.peel();
break;
case "banana":
// Bananas must be yellow in our example
const color: "yellow" = fruit.color
console.log(color);
// Bananas must not be red.
// typings:expect-error
const wrongColor: "red" = fruit.color
console.log(wrongColor);
// We can peel a banana
fruit.peel();
// But polishing a banana would be silly.
// typings:expect-error
fruit.polish();
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment