typescript notes (typeof, keyof, ReturnType, Parameters, Extract)
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:
const myFunc = (name: string) => {
return `hello ${name}`;
};
type the function into MyFunc
type MyFunc = typeof myFunc;
type a function return type (using built in ReturnType)
type MyFuncReturn = ReturnType<typeof myFunc>; // string
When the function changes, the types change!
You can also type the parameters using utility type Parameters
but Parameters will return a tuple (array), so you could index result like so: MyFuncParams[0]
- i.e. can use indexes on tuple types
Parameters is useful to extract type info from code you don't control e.g. external libraries
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
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 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">
- extract from a union all which don't match e.g. all but success
- type LoadingEvent = Exclude<Event, { status: "success"} >;
- 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 } } };
typeAge =Person ["age"];
typeI1 =Person ["age" | "name"]- type
I2 =Person [keyofPerson ]; - 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
constreq = {url : "https://example.com",method : "GET" }
asconst ;
- its like regex for types!
- you could define a type check to ensure that routes must start with a "/" e.g. "/", "/users" etc
- create type for those routes which have substitution param
type DynamicRoutes = Extract<Routes, `${string}:${string}`>;
// "/users/:id" | "/posts/:id"
- union of strings of all permutations (this is wild)
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
type Path = "Users/John/Documents/notes.txt";
type SplitPath = S.Split<Path, '/'>; // ["Users", "John", "Documents", "notes.txt"]
- turn string literals into object with names as keys
Comments
Post a Comment