The typeof and keyof operators - referencing variable types in Typescript
A practical guide on how you can use Typescripts keyof and typeof operators to build types from variables.
Typescript has a great inference system, which will infer Types for variables when the variable doesn’t have an explicit type annotation. The process in which Typescript will determine the type of a variable is called Type Widening, which I have covered previously here. But the gist of it is that Typescript is going to use the information it has to that point to infer the type of the variable; let’s see an example:
If we declare a variable using let
, Typescript will choose a broader type for the variable compared to when we declare a variable using const
, where it will choose a narrow type, as shown below:
As you can see above, the variable type declared using const is narrower, 1 in our case, compared to the one declared using let, number in our case. This is because the first variable is mutable; hence Typescript infers a more suitable type, so you can mutate it, while the second is immutable hence the stricter type inferred.
With that information in mind, what if we wanted to pass either of the two variables above to a function (or get their types) without explicitly annotating them?
typeof
Operator
How do we do this, you ask? Enters typeof
operator? Not to be confused with the Javascript’s typeof
operator, this allows you to refer to a type of a variable, and you can even create a Type from that.
Let’s see it in action on the variables we declared above:
type X = typeof x;
type Y = typeof y;
And these are the results:
As you can see, we created two Types - X
and Y
- and used the typeof
operator to reference the types of variables x
and y
. And we can use it to reference types for all sorts of things - objects
, arrays
, object
properties, etc.
So far, so good.
But we can do even better, and let’s introduce another Typescript operator - the keyof
operator.
The keyof
Operator
With the typeof
operator, we can get types of object property values, as shown in our previous example. The keyof
operator allows us to refer to an object keys type and returns a union of all keys of an object.
Let’s take the following example.
type Obj = {
firstName: string;
age?: number;
}
type ObjKeys = keyof Obj;
I know it’s a Type, but we will get to the good part; patience, please. The type of ObjKeys is now this:
type ObjKeys = "firstName" | "number";
Now, we can combine both the keyof
and typeof
operator to get the keys of an object variable as shown below:
const obj = {
firstName: "name",
age: 20
}
type ObjKeys = keyof typeof obj;
And now, the type of our ObjKeys is the same as what we had previously:
Now we can use this to refer to our object keys and create more type-safe code:
function getObjField(key: keyof typeof obj) {
return obj[key]
}
If we used a string as the type of key in the above function, we would get the following error:
Now, let’s take this a little further and talk about tuples:
const Assertions and Tuples
We have previously covered tuples in this newsletter; you can find the previous issue here. In this section, I want to show you you can combine arrays and const assertions to get back tuples.
const
assertion allows us to tell Typescript a variable is read-only or immutable, and Typescript won’t allow us to mutate the content of the variable in question.
Let’s see an example of a const assertion for an object below:
const obj = {
firstName: "name",
age: 20
} as const;
type ObjKeys = typeof obj;
We get a tuple back if we use const assertion with an array, as shown below:
const arr = [1, "ehllo", false] as const
type Arr = typeof arr;
And we can take this a step further and convert the array we started with to a union using indexed access types, which enables us to look up a specific property of another property, in our case, the content of the tuple, as shown below:
type ArrUnion = typeof arr[number]
And here are the results:
Conclusion
In this issue, we have seen how to reference a variable’s Types and pass the Types around to increase safety. This is great for when variables are the source of truth; for instance, you want Typescript to update the types when the variables change or when they come from outside your organization, i.e., third-party libraries.
And that’s it from me; I hope you enjoyed the content above as much as I did writing it, and I hope you will have a fantastic week ahead.