TypeScript: keyof / typeof

TypeScript: keyof / typeof
TypeScript: keyof / typeof

Lets talk about the usage of keyof and typeof in TypeScript. First of all we need to understand the basics of literal types and union of literal types.

Literal types

Basically it allow us to specify exact value that a type can have, it can be one of these 3 types string, number, boolean.

/**
* string literal
* the variable of type 'Language' can be only a 'string' and has a value of 'JavaScript'.
*/
type Language = 'JavaScript';
const myLanguage: Language = 'JavaScript';
const myLanguage: Language = 'Java'; // Error: Type '"Java"' is not assignable to type '"JavaScript"'.(2322)
/**
* number literal
* the variable of type 'BoxSize' can be only a 'number' and has a value of 4.
*/
type BoxSize = 4;
const boxSize: BoxSize = 4;
const anotherBoxSize: BoxSize = 5; // Error: Type '5' is not assignable to type '4'.(2322)
/**
* boolean literal
* the variable of type 'IsValid' can be only a 'boolean' and has a value of true.
*/
type IsValid = true;
const isFormControlValid: IsValid = true;
const isValidFormControl: IsValid = null; // Error: Type 'null' is not assignable to type 'true'.(2322)
view raw literal-type.ts hosted with ❤ by GitHub

In practice literal types are combines with union types, type guards, and type aliases in order to get an enum-like behavior

// string literal with union type
type UserRoles = 'User' | 'Admin' | 'Moderator';
const userRole: UserRoles = 'Admin';
// number literal with union type
type BoxSizes = 4 | 8 | 12;
const box: BoxSize = 12;
// boolean literal with union type
type IsValid = true | false;
const isFormValid: IsValid = false;

keyof

Returns a new type of T that is a union of literal types and these literal types are the names of the properties of T.

interface User {
id: number;
name: string;
age: number;
}
type UserKeys = keyof User;
let userProp: UserKeys;
userProp = 'id';
userProp = 'name';
userProp = 'age';
userProp = 'address'; // Error: Type '"address"' is not assignable to type 'keyof User'.(2322)
view raw keyof.ts hosted with ❤ by GitHub

With using the keyof operator on the type User, this will return a new union of literal types “id” | “name” | “age” which are the properties of the interface User.

keyof can be used with classes as well. but it works only with public members of the class.

class User {
id: number = 1;
name: string = '';
}
type UserProps = keyof User;
const userProp: UserProps = 'name'
/**
* operator keyof work only with public members of the class.
* should not work with private keyword
*/
class Person {
private id: number = 1;
name: string = '';
}
type PersonProps = keyof Person;
const personProp: PersonProps = 'id;' // Error: Type '"id;"' is not assignable to type '"name"'.(2322)
/**
* operator keyof work only with public members of the class.
* should not work with static keyword
*/
class Vehicle {
static id: number = 1;
name: string = '';
}
type VehicleProps = keyof Person;
const vehicleProp: VehicleProps = 'id;' // Error: Type '"id;"' is not assignable to type '"name"'.(2322)
view raw keyof-class.ts hosted with ❤ by GitHub

typeof

As you know JavaScript has a typeof operator, but in TypeScript it behave differently.

/**
* typeof in JavaScript returns the type of the variable, in below example it is an object
*/
const volvoS60 = {
make: 'Volvo',
model: 'S60'
}
const t = typeof vehicle;
console.log(t); // object
/**
* typeof in TypeScript returns the type of the object,
* in below example it will return a type with two properties
* 'make: string' and 'model: string'
* and later based on this type we can create new cars
*/
type Car = typeof volvoS60;
/**
* type Car = {
* make: string;
* model: string;
* }
*/
const volvoXC90: Car = {
make: 'Volvo',
model: 'XC90'
};
view raw typeof.ts hosted with ❤ by GitHub

typeof & keyof

These operators can be used together when we don’t know the type of an object. let see the below example:

const volvoXC90 = {
make: 'Volvo',
model: 'XC90'
}
// typeof volvoXC90; returns the type { make: string; model: string }
// keyof typeof volvoXC90; return the literal type union { 'make' | 'model' }
type CarProps = keyof typeof volvoXC90;
let carProp: CarProps;
carProp = 'make';
carProp = 'model';
carProp = 'year'; // Error: Type '"year"' is not assignable to type '"make" | "model"'.(2322)
view raw keyof-typeof.ts hosted with ❤ by GitHub

Here is another example of typeof keyof on an enum

enum Transmissions {
automatic = 'automatic',
manual = 'manual'
}
type CarTransmissions = keyof typeof Transmissions;
const volvoS60Transmission: CarTransmissions = 'manual';
const volvoXC90Transmission: CarTransmissions = 'automatic';

And here is an example with Generic type. In the following example, we have a function checkData that checks all properties and returns an object with each key and a boolean value.

const volvoXC60 = {
make: 'Volvo',
model: 'XC60',
year: 2016,
transmission: 'automatic'
};
declare function checkData<T>(data: T): { [key in keyof T]: boolean };
const checkVolvoXC60Data = checkData<typeof volvoXC60>(volvoXC60);
/**
checkVolvoXC60Data will have the following type
const checkVolvoXC60Data: {
make: boolean;
model: boolean;
year: boolean;
transmission: boolean;
}
*/

Thanks for reading — I hope you found this article useful. Happy coding! :)