Into the Unknown
@phenomnominal 2022
Into the Unknown
Hi Everyone,
I'm Craig !
- he/him
- twitter/phenomnominal
- github/phenomnomnominal
Hi Everyone,
I'm Craig !
@phenomnominal 2022
I'm going to tell you a story!
I'm going to tell you a story!
Chapter One
Chapter One
A very helpful reindeer
A very helpful reindeer
It was nearing the end of winter in the Kingdom of Arendelle...
It was nearing the end of winter in the Kingdom of Arendelle...
@phenomnominal 2022
Sven the reindeer was feeling very productive!
Sven the reindeer was feeling very productive!
@phenomnominal 2022
He had been helping his best friend Kristoff, the Royal Ice Master!
He had been helping his best friend Kristoff, the Royal Ice Master!
@phenomnominal 2022
Kristoff and Sven had been friends since they were very little...
Kristoff and Sven had been friends since they were very little...
@phenomnominal 2022
And it was their job to make sure that Arendelle had enough ice!
And it was their job to make sure that Arendelle had enough ice!
@phenomnominal 2022
Sven was always looking for ways to work smarter...
Sven was always looking for ways to work smarter...
@phenomnominal 2022
And he could always count on help from Olaf the Snowman!
And he could always count on help from Olaf the Snowman!
@phenomnominal 2022
Less work means more time for adventures!
Less work means more time for adventures!
@phenomnominal 2022
sven and Olaf LOVE adventures...
sven and Olaf LOVE adventures...
@phenomnominal 2022
Chapter Two
Chapter Two
Sven's most magical idea
Sven's most magical idea
Sven and Olaf had two other very special friends in the kingdom
Sven and Olaf had two other very special friends in the kingdom
@phenomnominal 2022
Princess Anna!
Princess Anna!
@phenomnominal 2022
And Queen Elsa!
And Queen Elsa!
@phenomnominal 2022
Anna was the brave, tenacious princess of Arendelle.
Anna was the brave, tenacious princess of Arendelle.
@phenomnominal 2022
and Queen Elsa was her big sister!
and Queen Elsa was her big sister!
@phenomnominal 2022
Queen Elsa had amazing powers!
Queen Elsa had amazing powers!
@phenomnominal 2022
She could sense the water molecules around her,
She could sense the water molecules around her,
@phenomnominal 2022
Transform the water molecules into ice,
Transform the water molecules into ice,
@phenomnominal 2022
And control that ice however she liked!
And control that ice however she liked!
@phenomnominal 2022
Sven would never forget the Day that Elsa
explained her
magic...
Sven would never forget the Day that Elsa
explained her
magic...
@phenomnominal 2022
"I'm going to let you in on my little secret"
@phenomnominal 2022
"You may have heard that any sufficiently advanced technology is indistinguishable from magic,"
"but actually, it's the other way around..."
@phenomnominal 2022
"Any sufficiently incredible magic, is actually technology!"
@phenomnominal 2022
"And the technology that makes my snow magic work is called..."
"TypeScript"
@phenomnominal 2022
Sven already Knew TypeScript from his work running the website for the local sauna, and he couldn't wait to learn more!
Sven already Knew TypeScript from his work running the website for the local sauna, and he couldn't wait to learn more!
@phenomnominal 2022
Sven and Olaf wanted to use Elsa's magic to make Kristoff's life easier!
Sven and Olaf wanted to use Elsa's magic to make Kristoff's life easier!
@phenomnominal 2022
and they had figured out the perfect plan!
and they had figured out the perfect plan!
@phenomnominal 2022
They were so excited to share it with Anna and Elsa!
They were so excited to share it with Anna and Elsa!
@phenomnominal 2022
*Snort*
@phenomnominal 2022
"That's a great idea Sven!"
"You can take Elsa's TypeScript ice magic, and create a library that anyone can use!"
"This is going to save so much time!"
Anna was thrilled!
Anna was thrilled!
@phenomnominal 2022
"Let's do this!"
"Anna and I will help you learn about TypeScript!"
Elsa couldn't wait to help!
Elsa couldn't wait to help!
@phenomnominal 2022
Chapter Three
Chapter Three
You can't freeze everything
You can't freeze Everything
Sven and Olaf got straight to work on their new library!
Sven and Olaf got straight to work on their new library!
@phenomnominal 2022
import { freeze, thaw } from '@queen-elsa/frozen';
const thing = {};
const frozen = freeze(thing);
const thawed = thaw(frozen);
They designed their ideal API...
They designed their ideal API...
@phenomnominal 2022
export function freeze (toFreeze: any): any {
return doElsaMagic(toFreeze);
}
// TODO (Sven): Ask Elsa to write later...
function doElsaMagic (toFreeze) {
return toFreeze;
}
They started building the functionality...
They started building the functionality...
@phenomnominal 2022
"This is going GREAT! We're amazing!"
"Anna and Elsa, what do you think?!"
@phenomnominal 2022
"Do you want to make it even better?!"
"This is really good! Great work!"
"Let's talk about Developer Experience..."
@phenomnominal 2022
import { freeze } from '@queen-elsa/frozen';
import { MagicFire } from '@bruni/fire';
const fire = new MagicFire();
const frozenFire = freeze(fire);
"What would happen if a user imported some Magic Fire...
and tried to freeze it?"
@phenomnominal 2022
"If they were to try to freeze MagicFire, there would be serious consequences, but our library allows it!"
"It's our job as library authors to save our users from these kinds of traps!"
"You and I know that you can't freeze MagicFire, but our users may not know that!"
@phenomnominal 2022
"Right now, we're using the any type:"
"But that's okay! It's not easy to see how best to type this code!"
export function freeze (toFreeze: any): any {
return doElsaMagic(toFreeze);
}
@phenomnominal 2022
export function freeze (toFreeze: any): any {
return doElsaMagic(toFreeze);
}
"We want to be able to freeze almost any input..."
"Right now the code says you can freeze absolutely anything!
@phenomnominal 2022
*grunt*
@phenomnominal 2022
"That's right Sven, we need to find some way to limit the kind of objects that can be frozen, so that no one can freeze something that is unfreezable!"
@phenomnominal 2022
export function freeze (toFreeze: any): any {
if (toFreeze.unfreezable) {
throw new FrozenError(`Can't freeze that! ❄️`);
}
return doElsaMagic(toFreeze);
}
"One thing we could do is catch the problem at run-time:"
"But we can do better!"
@phenomnominal 2022
"It would be better for the library user to know about the issue before they run the code..."
"That's where TypeScript can help! Anna, why don't you explain?"
@phenomnominal 2022
"TypeScript has lots of different types!"
"There's all the primitive types, like boolean and string,"
"There's all the built-in types, like Array and Date,"
"And then there's all the custom user-defined types!"
@phenomnominal 2022
"These types all form the TypeScript Type System!"
"One way to think about it is like a Venn diagram!"
@phenomnominal 2022
*hrrmpf*
@phenomnominal 2022
"Not a Sven diagram, Sven, a Venn Diagram!"
"Let's check it out!"
@phenomnominal 2022
boolean
number
Array
string
Date
any
@phenomnominal 2022
"The any type is what is called a top type of the type system."
"It is a type for which all values are valid."
"It is a super-type of all other types!"
@phenomnominal 2022
"Using the any type tells TypeScript that any value can be assigned to the variable."
"When TypeScript sees an any, it basically stops type-checking from that point on!"
@phenomnominal 2022
"The any type makes our code very flexible, but it can be dangerous!"
export function freeze (toFreeze: any): any {
// This may break at run-time,
// but not at compile-time
if (toFreeze.unfreezable) {
// throw ...
}
return doElsaMagic(toFreeze);
}
"If the input is undefined, our code will break, but the compiler doesn't complain!"
@phenomnominal 2022
"Is that possible?!"
"So we want our library to be flexible, but without the danger?!"
@phenomnominal 2022
"Yes! TypeScript gives us another tool for this!"
"Let's look at the Venn diagram again..."
"We can use the unknown type!"
@phenomnominal 2022
boolean
number
Array
string
Date
any
unknown
@phenomnominal 2022
"The unknown type is also a top type of the type system. It is also a super-type of all other types! All values are also valid!"
"But it behaves a little bit differently...
@phenomnominal 2022
"And we get a compile-time error! We can't use the unfreezable property, because the input might not exist!"
export function freeze (toFreeze: unknown): unknown {
// This may break at run-time,
// so now we get an error at compile-time!
if (toFreeze.unfreezable) {
// throw ...
}
return doElsaMagic(toFreeze);
}
"We can replace the any with unknown..."
@phenomnominal 2022
"So, we need to fix our compile-time error! We need a way for the compiler to decipher the unknown value."
"One way to do this is to use a type guard!"
@phenomnominal 2022
"A type guard is a special kind of function that gives TypeScript hints about how to narrow a type."
"It tells TypeScript how to go from a broad type - like any or unknown - to a specific type!"
@phenomnominal 2022
function isUnfreezable (thing: unknown): thing is Unfreezable {
return thing && (thing as Unfreezable).unfreezeable;
}
type Unfreezable = {
unfreezable: true
}
export function freeze (toFreeze: unknown): unknown {
if (!isUnfreezable(toFreeze)) {
// toFreeze is freezable!
}
// throw ...
}
"A type guard is a run-time check that maps to a type:"
Special is keyword
@phenomnominal 2022
function isUnfreezable (thing: unknown): thing is Unfreezable {
return thing && (thing as Unfreezable).unfreezeable;
}
type Unfreezable = {
unfreezable: true
}
export function freeze (toFreeze: unknown): unknown {
if (!isUnfreezable(toFreeze)) {
// toFreeze is freezable!
}
// throw ...
}
"Right now, the implementation is a little weird..."
Weird double negative...
Confusing control flow...
@phenomnominal 2022
"It would be better if we could talk about something being freezable!"
"TypeScript has another handy type to help. Let's go back to our Venn diagram..."
@phenomnominal 2022
boolean
number
Array
string
Date
unknown
any
never
"The never type is the bottom type of the
type system. It
is a type which
has no valid
values!"
@phenomnominal 2022
type Freezable = {
unfreezable: never
}
"We can use the never type to describe what a freezable object is:"
"An object with a value for to the unfreezable property is never assignable to Freezable!"
@phenomnominal 2022
"This means we can invert our type guard:"
// Before:
function isUnfreezable (thing: unknown): thing is Unfreezable {
return thing && (thing as Freezable).unfreezeable;
}
// After:
function isFreezable (thing: unknown): thing is Freezable {
return thing && !(thing as Freezable).unfreezeable;
}
export function freeze (toFreeze: unknown): unknown {
if (isFreezable(toFreeze)) {
// toFreeze is freezable!
}
// throw ...
}
No more double negative!
@phenomnominal 2022
"Another way to use never is for the return type of a function that never returns:"
function throwError (error: string): never {
throw new FrozenError(error);
}
export function freeze (toFreeze: unknown): unknown {
if (isFreezable(toFreeze)) {
// toFreeze is freezable!
}
throwError(`Can't freeze that! ❄️`);
}
@phenomnominal 2022
"Assertion guards combine the ideas from type guards and never:"
function assertFreezable (thing: unknown): asserts thing is Freezable {
if (!thing || (thing as Freezable).unfreezable) {
throw new FrozenError(`Can't freeze that! ❄️`);
}
}
export function freeze (toFreeze: unknown): unknown {
assertFreezable(toFreeze);
// toFreeze is freezable!
return doElsaMagic(toFreeze);
}
Special asserts keyword
More sensible flow!
@phenomnominal 2022
"That's great! But we can still go further!"
export function freeze (toFreeze: unknown): unknown {
assertFreezable(toFreeze);
return doElsaMagic(toFreeze);
}
freeze({ unfreezable: true }); // run-time error!
"Our input is still typed as unknown..."
@phenomnominal 2022
"We can restrict our input to the Freezable type:"
export function freeze (toFreeze: Freezable): unknown {
assertFreezable(toFreeze);
return doElsaMagic(toFreeze);
}
freeze({ unfreezable: true }); // compile-time error!
"Now we have run-time and compile-time safety checks!"
@phenomnominal 2022
Chapter Four
Chapter Four
To be frozen
To be frozen
Sven and Olaf were delighted!
Sven and Olaf were delighted!
@phenomnominal 2022
"Next, we need to talk about what is involved with freezing something!"
"I use a very obscure dialect of TypeScript, that looks something like this..."
@phenomnominal 2022
([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(![]+[])[+!+[]]+(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]
"You'll need to trust me that it works!"
@phenomnominal 2022
function doElsaMagic (toFreeze: Freezable): unknown {
const result = ([]+[])[(![]+[])[+[]...
(toFreeze as any).frozen = result;
return toFreeze as unknown;
}
"We can simplify it down to understand what's going on:"
@phenomnominal 2022
function doElsaMagic(toFreeze: Freezable): unknown {
const result = ([]+[])[(![]+[])[+[]...
(toFreeze as any).frozen = result;
return toFreeze as unknown;
}
"But now we have some messy types to fix!"
Oh no!
@phenomnominal 2022
"We already have a Freezable type, but we need to replace the unknowns and any..."
function doElsaMagic(toFreeze: Freezable): unknown {
const result = ([]+[])[(![]+[])[+[]...
(toFreeze as any).frozen = result;
return toFreeze as unknown;
}
"We need a Frozen type!"
@phenomnominal 2022
type BasicFrozen = {
frozen: true;
}
"But it doesn't really express what we mean..."
"The most basic version could look like this:"
@phenomnominal 2022
"We want to combine the type of the Freezable thing, with the BasicFrozen type..."
Freezable
BasicFrozen
Freezable and BasicFrozen
@phenomnominal 2022
"We can use TypeScript to express this, by using generic types and intersection types..."
type Frozen<T> = T & {
frozen: true;
}
T is a convention, but it could be named anything!
& is the syntax for an intersection type
@phenomnominal 2022
type Frozen<BaseType> = BaseType;
function Frozen (baseType) {
return baseType;
}
type Frozen<BaseType> = BaseType & {
frozen: true;
}
function Frozen (baseType) {
return {
...baseType,
frozen: true
};
}
"It can be useful to think about complex types in equivalent JavaScript syntax:"
@phenomnominal 2022
type Frozen<T> = T & {
frozen: true;
}
function freeze<T>(toFreeze: T): Frozen<T> {
assertFreezable(toFreeze);
(toFreeze as Frozen<T>).frozen = ([]+[])[(![]+[])[+[] ...;
return toFreeze as Frozen<T>;
}
"And now we can almost correctly express our function:"
"Do you see what is missing?"
@phenomnominal 2022
"It doesn't say that the type of the input has to be Freezable?"
@phenomnominal 2022
"Exactly! For that we need to express one more type relationship:"
Freezable
Super-type of Freezable
@phenomnominal 2022
"For that we can use the extends keyword:"
type Frozen<T extends Freezable> = T & {
frozen: true;
}
function Frozen (baseType: Freezable) {
return {
...baseType,
frozen: true
};
}
Uses the extends keyword just like with classes!
@phenomnominal 2022
"We can even set a default type for the type parameter:"
type Frozen<T extends Freezable = Freezable> = T & {
frozen: true;
}
function Frozen (baseType: Freezable = {}) {
return {
...baseType,
frozen: true
};
}
@phenomnominal 2022
"That gives us everything we need for the freeze function!"
@phenomnominal 2022
type Freezable = {
unfreezable: never
}
type Frozen<T extends Freezable> = T & {
frozen: true;
}
function assertFreezable (thing: unknown): asserts thing is Freezable {
if (!thing || (thing as Freezable).unfreezable) {
throw new FrozenError(`Can't freeze that! ❄️`);
}
}
function freeze <T extends Freezable> (toFreeze: T): Frozen<T> {
assertFreezable(toFreeze);
(toFreeze as Frozen<T>).frozen = ([]+[])[(![]+[])[+[] ...;
return toFreeze as Frozen<T>;
}
Freezable type! An object with a unfreezable property is never assignable to Freezable
Frozen type! A Frozen object is the intersection of a Freezable object and an object with the frozen property set to true
Assert Freezable function! An Assertion Guard that takes an unknown input and throws an error if the object is not Freezable
Freeze function! A function that takes a Freezable input, asserts that it is Freezable, freezes the object, and returns a Frozen type
@phenomnominal 2022
"The library is finished!?"
"Come on Sven, let's go show Kristoff right away!"
Olaf was very excited!
Olaf was very excited!
@phenomnominal 2022
And before Anna and Elsa could even try to stop them, Sven and Olaf were out the door and on their way...
And before Anna and Elsa could even try to stop them, Sven and Olaf were out the door and on their way...
@phenomnominal 2022
Chapter Five
Chapter Five
2 fast 2 frozen
2 fast 2 frozen
Olaf and Sven rushed to show Kristoff their library!
Olaf and Sven rushed to show Kristoff their library!
@phenomnominal 2022
*snort*
"Hi Kristoff! We made you a library to help make your job easier!"
@phenomnominal 2022
import { freeze } from '@queen-elsa/frozen';
const thing = {};
const frozenThing = freeze(thing);
const unfreezableThing = { unfreezable: true };
freeze(unfreezableThing); // compile-time error!
"You can use it to freeze anything you like, just like Elsa!"
@phenomnominal 2022
"You made this for me!?"
Kristoff was blown away!
Kristoff was blown away!
"Let's try it out on me right now!"
@phenomnominal 2022
Kristoff is quite the rockstar developer, so he wrote a little script very quickly...
Kristoff is quite the rockstar developer, so he wrote a little script very quickly...
@phenomnominal 2022
But just as he ran the script, Anna and Elsa burst in!
But just as he ran the script, Anna and Elsa burst in!
@phenomnominal 2022
"Wait! Stop!"
"We still need to write the thaw function!"
@phenomnominal 2022
But it was too late...
The library worked...
But it was too late...
The library worked...
@phenomnominal 2022
Olaf was frozen.
Olaf was frozen.
@phenomnominal 2022
Chapter Six
Chapter Six
Do you want to thaw a snowman?
Do you want to thaw a snowman?
"Oh no, what have I done!? I've frozen Olaf!"
Kristoff was devastated
Kristoff was devastated
@phenomnominal 2022
"We need to move fast!"
"Or Olaf could be frozen forever..."
@phenomnominal 2022
function thaw (toThaw) {
assertFrozen(toThaw);
delete toThaw.frozen;
return toThaw;
}
function assertFrozen(thing) {
if (!thing || !thing.frozen) {
throw new FrozenError(`Can't thaw that! ❄️`);
}
}
"Quickly, let's start with the JavaScript:"
@phenomnominal 2022
"Can't we just add any everywhere and fix it!?"
"It's too risky, we don't want something going wrong!"
@phenomnominal 2022
@phenomnominal 2020
"Sven, why don't you add types to the assertion guard first!"
*gulp*
@phenomnominal 2022
@phenomnominal 2020
function assertFrozen(thing: unknown): asserts thing is Frozen {
if (!thing || !(thing as Frozen).frozen) {
throw new FrozenError(`Can't thaw that! ❄️`);
}
}
Add the unknown type to the input
Cast the input to Frozen to do the check
Add the asserts statement for the return type
@phenomnominal 2022
"That's perfect!"
*clip clop!*
@phenomnominal 2022
"Now we need to add types to the thaw function!"
"Kristoff, I need your help!"
@phenomnominal 2022
function thaw<T extends Frozen>(toThaw: T) {
assertFrozen(toThaw);
delete toThaw.frozen;
return toThaw;
}
"We can start by adding a generic type for the input that has to extend Frozen?"
"But I have no idea how to do the return type! Help!"
@phenomnominal 2022
"We need to use TypeScript to extract the type of the original object from the Frozen type!"
"This is the perfect time to use..."
"Conditional Types!"
@phenomnominal 2022
T extends X ? Y : Z;
Condition
Pass
Fail
"Conditional types let us change type based on the state of other types!"
@phenomnominal 2022
type Foo<T> = T extends X ? Y : Z;
function fooType (T) {
if (T === X) {
return Y;
} else {
return Z;
}
}
"A Conditional types is like a type function with an if statement!"
@phenomnominal 2022
"Conditional Types have an extra special power..."
"Type inference!"
@phenomnominal 2022
"Type inference gives us a mechanism to extract types from other types!"
"Imagine that we want to extract the type of the return value of a function..."
@phenomnominal 2022
type ReturnType<T> =
T extends (...args: Array<any>) => infer R
? R
: never;
function one () {
return 1;
}
type A = ReturnType<typeof one>; // number
The infer tells TypeScript to work it out for us!
@phenomnominal 2022
type Parameters<T> =
T extends (...args: infer P) => any
? P
: never
function add (a: number, b: number) {
return a + b;
}
type P = Parameters<typeof add>; // [number, number]
@phenomnominal 2022
type Thawed<T> =
T extends Frozen<infer R>
? R
: never;
type Water = {};
type Ice = Frozen<Water>;
type W = Thawed<Ice>; // Water
"We can use type inference to extract the type of the generic Frozen object:"
@phenomnominal 2022
type Thawed<T> = T extends Frozen<infer R>
? R
: never;
function thaw<T extends Frozen>(toThaw: T): Thawed<T> {
assertFrozen(toThaw);
delete toThaw.frozen;
return toThaw as Thawed<T>;
}
"Okay, I got this! I'll update the thaw function!"
Add the Thawed conditional type
Use Thawed as the return type
And cast the result!
@phenomnominal 2022
"That looks perfect to me!"
"Let's save Olaf, before it's too late!"
@phenomnominal 2022
Chapter Seven
Chapter Seven
That was weird
That was weird
Kristoff, Sven, Anna & Elsa all worked together to quickly write a new script...
Kristoff, Sven, Anna & Elsa all worked together to quickly write a new script...
@phenomnominal 2022
Thanks to the library's excellent types, they were confident that the code was good...
Thanks to the library's excellent types, they were confident that the code was good...
@phenomnominal 2022
So they ran the script...
So they ran the script...
@phenomnominal 2022
And just like that, Olaf was back to his normal self!
And just like that, Olaf was back to his normal self!
@phenomnominal 2022
"Hi everyone, my name's Olaf!"
"What happened?"
"That was weird!"
@phenomnominal 2022
"Kristoff, I think you'd better reccap!"
"Oh Olaf! We're so glad you're okay! So much has happened!"
@phenomnominal 2022
@phenomnominal 2022
Prefer unknown over any when you don't know what type something is!
*
*
*The never type can be used to describe types that cannot happen!*
We can use Type Guards and Assertion Guards to narrow types based on run-time behaviour
@phenomnominal 2022
We can use Generics to help describe unknown types. Sub-types, union type and intersection types are also really useful!
*Conditional types and type inference can also be really powerful*
It's worth putting in the effort with your types so that people who consume your code have a good experience!
@phenomnominal 2022
"Neat!"
The End
The End
The End
Thanks everyone!
Thanks everyone!
@phenomnominal 2022
Copy of Into the Unknown! - CODEstantine 6
By Craig Spence
Copy of Into the Unknown! - CODEstantine 6
Craig Spence - CODEstantine 6 - Into The Unknown
- 2,606