Skip to content

Instantly share code, notes, and snippets.

@gvergnaud
Last active February 14, 2023 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gvergnaud/9865ed6262ef51b28ee6d7b786adf4d7 to your computer and use it in GitHub Desktop.
Save gvergnaud/9865ed6262ef51b28ee6d7b786adf4d7 to your computer and use it in GitHub Desktop.
// 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 {};
// 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 {};
// 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 {};
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