If you are already familiar with TypeScript, you must know that it provides different ways to type objects. You can use a type or an interface to strongly type your objects:
type MyObject = {
foo: string;
bar: boolean;
};
interface MyObject {
foo: string;
bar: boolean;
}
Sometimes we also use objects as key-value stores where we don't know their properties beforehand. In such cases Record utility type provided by TypeScript comes in handy:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
There's a catch though. If you use string as its key, TypeScript will assume that your object holds a value for every possible string. Consider the following example for clarity:
type Colors = Record<string, string>;
const colors: Colors = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF"
};
console.log(colors.red); // "#FF0000"
console.log(colors.yellow); // undefined
As you can see, TypeScript gives no error for colors.yellow and expects that it is a string. However it is not. We don't have a value for yellow in our object. This is especially dangerous if you try to reach to a property of value. In such case we may have Uncaught TypeError exception and our application may crush.
console.log(colors.red.toLowerCase()); // "#ff0000"
console.log(colors.yellow.toLowerCase()); // Uncaught TypeError: can't access property "toLowerCase" of undefined
Solution: Use Partial
To avoid such cases, we can combine Record with another utility type, Partial:
type Colors = Partial<Record<string, string>>;
Now we can use our Colors type safely and let TypScript to warn us about possible type errors.


Top comments (0)