Last active
June 7, 2026 19:29
-
-
Save denisos/b068303ff4e6fc9fe02526ad490db914 to your computer and use it in GitHub Desktop.
Typescript examples of useful basic language features
This file contains hidden or 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
| // typeof | |
| // extract a type from runtime code, typeof "make a type of it" (i.e. from it) | |
| // e.g. typeof "Hello world" => string | |
| // only legal on variable names | |
| // can be used with functions or any other expressions | |
| // aside: typeof vs keyof: keyof takes a type and creates a string/number union of it's keys. So keyof creates a new type from a type ....whereas typeof creates a type from an expression. good post | |
| // Given a function below type the function into MyFunc. When the function changes, the types change! | |
| const myFunc = (name: string) => { | |
| return `hello ${name}`; | |
| }; | |
| type MyFunc = typeof myFunc; | |
| // type a function return type (using built in ReturnType) | |
| type MyFuncReturn = ReturnType<typeof myFunc>; // string | |
| // Parameters | |
| // You can also type the parameters using utility type Parameters | |
| // Parameters is useful to extract type info from code you don't control e.g. external libraries | |
| // but Parameters will return a tuple (array), so you could index result like so: MyFuncParams[0]. i.e. can use indexes on tuple types | |
| type MyFuncParams = Parameters<typeof myFunc>; // string | |
| // Promises | |
| // The return type of an async function is a promise (which wraps the result returned), so use the global Promise type when typing it | |
| // Awaited utility type is a way to unwrap Promises to get the returned type e.g. from async functions | |
| // Create a union type from keys of an object | |
| // using keyof and typeof together can give us the type we want, which updates automatically when the object changes | |
| // in this example myKeyedObject instance is converted to a type and then we use keyof to access the keys | |
| const myKeyedObject = { name: "denis", age: 40 }; | |
| type MyKeyedObjectKeys = keyof typeof myKeyedObject; | |
| MyKeyedObjectKeys // "name" | "age" | |
| // here's a case where we get all the values of the keys of an object using indexed access keyof as a shortcut | |
| type Obj = typeof myKeyedObject; | |
| type MyKeyedObjectKeyTypes = Obj[keyof Obj] | |
| MyKeyedObjectKeyTypes // "string" | "number" | |
| // note if we had added "as const" to end of myKeyedObject then the last line ^ would have returned literals "denis" | "40" (instead of "string" | "number" ) | |
| // here's a cool helper to get values as types using a ValueOf generic | |
| type ValueOf<T> = T[keyof T]; | |
| const myObj = { | |
| start: "startedit", | |
| created: "createdit", | |
| paid: "paidit" | |
| } as const; | |
| type MyObjType = typeof myObj | |
| type ObjValue = ValueOf<MyObjType>; // "startedit" | "createdit" | "paidit" | |
| type ObjKey = keyof MyObjType; // "start" | "created" | "paid" | |
| // ...the ValueOf is same as: type ObjValue = MyObjType[keyof MyObjType] | |
| // Discriminating union | |
| // A union of types where each type in the union has a common field which is the "discriminator" to allow tell them apart | |
| // e.g. state where status could be one of "loading" | "success" | "error" (just an example) | |
| // within an if or switch (to narrow down) typescript will know which exact type you're using | |
| // Extract (utility type) | |
| // extract from a union | |
| type LoadingEvent = Extract<Event, { status: "loading"} >; | |
| // if any in union extend the 2nd param, then extract it. could end up with a union if more than one match | |
| type Fruit = "apple" | "banana" | "orange" | |
| type BananaAndOrange = Extract<Fruit, "banana" | "orange"> | |
| // if you just want to get the union type of a discriminated union then this will work: | |
| type StatusType = Event["status"]; // "loading" | "success" | "error" | |
| // ...this will also work for any common property in the types | |
| // Exclude (utility type) | |
| // extract from a union all which don't match e.g. all but success | |
| type LoadingEvent = Exclude<Event, { status: "success"} >; | |
| Indexed Access Types (on objects or arrays) | |
| access one or more properties on a type using indexed access type, ts docs | |
| e.g. | |
| type Person = { age: number; name: string; alive: boolean, addr: { st: number } } }; | |
| type Age = Person["age"]; | |
| type I1 = Person["age" | "name"] | |
| type I2 = Person[keyof Person]; | |
| you can go deep into a nested object to get a type | |
| type Z3 = Person['addr']['st'] | |
| also works indexing on an array! | |
| you can also convert an object to a type using typeof and then index to get types and create a union like this: | |
| type PE = typeof programModeEnumMap; | |
| export type IndividualProgram = PE["ONE_ON_ONE"] | PE['SELF_DIRECTED'] | | |
| PE['PLANNED_ONE_ON_ONE']; | |
| OR...pass a union to the type and get back a union | |
| export type IndividualProgram = typeof programModeEnumMap[ | |
| | "ONE_ON_ONE" | |
| | "SELF_DIRECTED" | |
| | "PLANNED_ONE_ON_ONE" | |
| | "PLANNED_SELF_DIRECTED" | |
| ] | |
| you can also index into arrays, e.g. | |
| const fruits = ["apple", "banana", "orange"] as const; | |
| type AppleOrBanana = typeof fruits[0 | 1]; | |
| type Fruit = typeof fruits[number]; | |
| as const | |
| convert entire object (or array) to type literals, in code below you can't write req.method = "POST" | |
| works with object nesting (unlike Object.freeze which only works on 1st level) | |
| as const freezes values as literal types and also adds a readonly annotation | |
| literal unions make use of this e.g. type Status = "loading" | "error"; | |
| you can combine with non-literal types | |
| const req = { | |
| url: "https://example.com", | |
| method: "GET" } | |
| as const; | |
| template literals | |
| its like regex for types! | |
| you could define a type check to ensure that routes must start with a "/" e.g. "/", "/users" etc | |
| type Route = `/${string}` | |
| create type for those routes which have substitution param | |
| type Routes = "/users" | "/users/:id" | "/posts" | "/posts/:id"; | |
| type DynamicRoutes = Extract<Routes, `${string}:${string}`>; | |
| // "/users/:id" | "/posts/:id" | |
| union of strings of all permutations (this is wild) | |
| type BreadType = "rye" | "brown" | "white"; | |
| type Filling = "cheese" | "ham" | "salami"; | |
| type Sandwich = `${BreadType} sandwich with ${Filling}`; | |
| // "rye sandwich with cheese" | "rye sandwich with ham" | etc | |
| using ts-toolbelt to split and join type literals | |
| import { S } from "ts-toolbelt"; | |
| type Path = "Users/John/Documents/notes.txt"; | |
| type SplitPath = S.Split<Path, '/'>; // ["Users", "John", "Documents", "notes.txt"] | |
| type BackAgain = S.Join<SplitPath, ':'> // "Users:John:Documents:notes.txt" | |
| turn string literals into object with names as keys | |
| type TemplateLiteralKey = `${"user" | "post" | "comment"}${"Id" | "Name"}`; | |
| type ObjectOfKeys = Record<TemplateLiteralKey, string>; // { userId: string, userName: string...} | |
| type UprObjectOfKeys = Record<Uppercase<Event>, string>; // uppercase |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment