Last active
February 14, 2023 15:56
-
-
Save gvergnaud/9865ed6262ef51b28ee6d7b786adf4d7 to your computer and use it in GitHub Desktop.
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
// a conditional type is just type-level code branching | |
// A extends B means "A is assignable to B" | |
type Test1 = "hello" extends string ? true : false; | |
// ^? | |
type Test2 = 2 extends string ? true : false; | |
// ^? | |
type Test3 = string extends "hello" ? true : false; | |
// ^? | |
type IsString<T> = T extends string ? true : false; | |
type Test4 = IsString<"hello">; | |
type Test5 = IsString<2>; | |
export {}; |
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
// type helpers are utility function! | |
// they solve generic problems: | |
type User = { name: string; age: number }; | |
type PartialUser = Partial<User>; | |
// ^? | |
type OmittedUser = Omit<User, "age">; | |
// ^? | |
type Action = "setAge" | "setName" | "getAge" | "getName"; | |
type NameAction = Extract<Action, `${string}Name`>; | |
// ^? | |
// Why build your own type helpers? | |
type AddId<T> = T & { id: string }; | |
type APIUser = AddId<User>; | |
// ^? | |
// you need to prettify: | |
type AddId2<T> = Prettify<T & { id: string }>; | |
type APIUser2 = AddId2<User>; | |
// ^? | |
// with higher level abstractions: | |
type AddId3<T> = FromEntries<Entries<T> | ["id", string]>; | |
type APIUser3 = AddId3<User>; | |
// ^? | |
/** | |
* | |
* My type helpers: | |
* | |
*/ | |
type Entries<T> = { | |
[K in keyof T]: [K, T[K]]; | |
}[keyof T]; | |
type FromEntries<Entries extends [PropertyKey, unknown]> = { | |
[E in Entries as E[0]]: E[1]; | |
}; | |
type Prettify<T> = { [K in keyof T]: T[K] } | never; | |
export {}; |
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
// Mapped Types are a way to turn a union type into an object, nothing more! | |
type Union = "a" | "b" | "c" | "d"; | |
// This is a Mapped Type | |
// 👇 | |
type Test = { | |
[Element in Union]: Element; | |
}; | |
// And it's equivalent to this type: | |
type Test2 = { a: "a"; b: "b"; c: "c"; d: "d" }; | |
// A mysterious name! | |
// Why are they called mapped types? the main usecase is to transform object types | |
type User = { name: string; age: number }; | |
type PartialUser = { | |
[Key in "name" | "age"]?: User[Key]; | |
}; | |
type PartialUser2 = { name?: string; age?: number }; | |
// The intermediary object pattern | |
// Transforming union types is pretty common | |
type TransformedUnion = { | |
[Letter in Union]: `letter is ${Letter}`; | |
}[Union]; | |
// "letter is a" | "letter is b" | "letter is c" | "letter is d" | |
// it turns out we don't really need that: | |
type TransformedUnion3 = `letter is ${Union}`; | |
// But there are use cases where this is useful: | |
type Entries<T> = { | |
[K in keyof T]: [K, T[K]]; | |
}[keyof T]; | |
type UserEntries = Entries<{ name: string; age: number }>; | |
// ^? | |
// Let's say we want exclude keys from users that are assignable to number. | |
// Filtering Object keys is easy, we can use `Omit`! | |
type Test3 = Omit<User, "age">; | |
// ^? | |
// but what if we want to exclude by values? | |
// how would we do that in JS? | |
// const entries = Object.entries(user) | |
// const filteredEntries = entries.filter(...) | |
// const newOblect = Object.fromEntries(filteredEntries) | |
// 1. Get the entries as a union of [key, value] pairs | |
// 2. Exclude those we don't want | |
// 3. Create an object from entries | |
type Test4 = FromEntries<Exclude<Entries<User>, [any, number]>>; | |
// ^? | |
type FromEntries<Entries extends [PropertyKey, unknown]> = { | |
[E in Entries as E[0]]: E[1]; | |
}; | |
// We can generalize over this pattern easily: | |
type OmitByValue<T, Condition> = FromEntries< | |
Exclude<Entries<T>, [any, Condition]> | |
>; | |
type Test5 = OmitByValue<User, number>; | |
// ^? | |
export {}; |
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
type User = { | |
name: string; | |
age: number; | |
}; | |
type Result = User["age"]; | |
// ^? | |
// Different version | |
function walkToTheOffice(action: "grabACoffee" | "keepWalking") { | |
const transitions = { | |
grabACoffee: "late", | |
keepWalking: "on time", | |
} as const; | |
const result = transitions[action]; | |
// ^? | |
console.log(result); | |
} | |
walkToTheOffice("grabACoffee"); // "late" | |
walkToTheOffice("keepWalking"); // "on time" | |
export {}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment