In this post, I’ll discuss keyof
, typeof
, mapped types, and conditional types, and show you how to use them in your daily work.
Introduction to Types in TypeScript
TypeScript (TS) is a superset of JavaScript that adds static typing to the language.
This means you can type your variables, functions, and objects, helping to catch errors at compile time.
But TypeScript is more than just basic types like strings or numbers. It also allows advanced typing techniques, such as keyof
, typeof
, mapped types, and conditional types.
Understanding Keyof in TypeScript
What is Keyof?
The keyof
keyword is an operator in TypeScript that returns a union of strings representing all keys of a given object type.
How to Use Keyof?
Suppose you have an interface:
interface User {
id: number;
name: string;
email: string;
}
When you use keyof User
, you get a type equivalent to 'id' | 'name' | 'email'
.
Example with Keyof
type UserKeys = keyof User; // 'id' | 'name' | 'email'
let key: UserKeys;
key = "name"; // OK
key = "address"; // Error: Type '"address"' is not assignable to type 'UserKeys'
With keyof
, you can create more advanced types that ensure you only use the available keys of an object.
What is Typeof in JavaScript?
In JavaScript, we have the typeof
operator, which returns a variable's type as a string, such as 'string'
, 'number'
.
In TypeScript, the typeof
operator can be used in typing contexts to read the type of a variable or object.
How to Use Typeof in TypeScript?
If you have a variable:
let user = {
id: 1,
name: "Alice",
email: "alice@example.com",
};
You can create a type based on this variable’s type:
type UserType = typeof user;
Now UserType
has exactly the same type as user
.
Example with Typeof
function getUser(): UserType {
return {
id: 2,
name: "Bob",
email: "bob@example.com",
};
}
typeof
allows you to assign the type of an existing variable to a new type.
What are Mapped Types in TypeScript?
Mapped types are types that allow you to map one type to another, transforming its properties.
Using Mapped Types: Partial, Required, Readonly, Pick, Omit
TypeScript provides several built-in utility types that are mapped types:
Partial<T>
: Makes all properties of typeT
optional.Required<T>
: Makes all properties of typeT
required.Readonly<T>
: Makes all properties of typeT
read-only.Pick<T, K>
: Creates a type with a subset of propertiesK
from typeT
.Omit<T, K>
: Creates a type fromT
without the propertiesK
.
Sample Mapped Type
Partial
type PartialUser = Partial<User>;
// All properties are optional
let user1: PartialUser = { name: "Charlie" };
Required
type RequiredUser = Required<User>;
// All properties are required
let user2: RequiredUser = {
id: 3,
name: "Dana",
email: "dana@example.com",
};
Pick
type UserNameAndEmail = Pick<User, "name" | "email">;
// Type with only 'name' and 'email'
let user3: UserNameAndEmail = {
name: "Eve",
email: "eve@example.com",
};
Omit
type UserWithoutEmail = Omit<User, "email">;
// Type with all properties except 'email'
let user4: UserWithoutEmail = {
id: 4,
name: "Frank",
};
Creating Custom Mapped Types
You can also create custom mapped types:
type MyMappedType<T> = {
[P in keyof T]: string;
};
// All properties of type T will be of type string
type StringifiedUser = MyMappedType<User>;
let user5: StringifiedUser = {
id: "5",
name: "Grace",
email: "grace@example.com",
};
Advanced Typing with Conditional Types
Understanding Conditional Types
Conditional types allow you to create types that depend on certain conditions.
The syntax is similar to the ternary operator in JavaScript:
T extends U ? X : Y
If T
is a subtype of U
, the result is X
; otherwise, it is Y
.
Example with Conditional Types
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
Uses of Conditional Types
Conditional types are very useful in the context of creating generic types that can change depending on the provided parameters.
Combining Keyof, Typeof, and Mapped Types
Creating a Type Based on Object Keys
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let user6 = { id: 6, name: "Hank", email: "hank@example.com" };
let userName = getProperty(user6, "name"); // Type string
Here, we used keyof
to restrict the key K
to existing keys of the object T
.
Using Typeof with Keyof
const COLORS = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF",
};
type ColorKeys = keyof typeof COLORS; // 'red' | 'green' | 'blue'
typeof
lets us create a type based on COLORS
, and keyof
extracts the keys of that type.
Summary
Learning keyof
and typeof
in TypeScript opens the door to more advanced typing techniques.
Mapped types, such as Partial
, Required
, Pick
, or Omit
, facilitate type manipulation and create more flexible code.
Conditional types add another level of dynamism, allowing for condition-dependent typing.
I hope this article has helped you understand these powerful tools in TypeScript. I encourage you to experiment and create your own solutions. If you have any questions or would like to share your experiences, leave a comment!