When working with a large and complex code base like AG Grid it is very easy to miss updating certain parts of the code base.
For example, we have code generators that depend on having accurate lists of interface property names. In other words given a Typescript interface
how can you produce an array that contains every property and ensure it stays up to date.
There are three approaches you might take to keeping this array of properties accurate:
1: Add a BIG comment reminding devs to update another part of the codebase.
// IF YOU UPDATE THE COLDEF INTERFACE REMEMBER TO ALSO UPDATE...
2: Delve deep into the world of ASTs to extract / generate the list based off the code file.
3: Use Typescript to enforce the link between ColDef
properties and a manually maintained list.
// See below for an explanation
type ColDefObject = Record<(keyof ColDef), undefined>;
1. Add a BIG COMMENT
The first approach is very simple and works initially. However, as the team grows, or you get forgetful it can still be easily missed. This is true no matter how big and attention grabbing you try to make your comment. So it's probably best not to go for this approach. Secondly, its litters the code base with unsightly comments.
2. Use an AST Parser and code Generator
The second approach requires a lot more work! Yes it will ensure the property list is perfectly accurate, but now you have to maintain a code parser and manage additional build complexity. While this isn't beyond the realms of possibility, (we have a few of these in AG Grid) it is definitely not an easy task. We can safely say that this approach is totally overkill for what we are trying to solve, especially with the next option.
3. Use Typescript to enforce the updates
The third option really is the best of both worlds. There is no need for a big comment because your Typescript build will fail if the property list is out of sync. Secondly, we don't need to write a code generator, we have a perfectly good human developer who can fix the issue in seconds.
What makes this very easy is that they will get autocompletion and a clear description of what property they need to add to the list. This approach is both simple to maintain and also is 100% reliable.
How to use Typescript
So how can we use Typescript to achieve our desired result? As a reminder we want to have a complete array of all the property names defined on the ColDef
interface.
It is worth clarifying we want an actual array of property names that we can use in our code as opposed to just having the list of possible properties in Type land. This is why (keyof ColDef)[]
alone is not sufficient.
First off lets show a cut down version of the ColDef
interface.
interface ColDef {
headerName: string;
columnGroupShow: boolean;
toolPanelClass: {string: string};
valueGetter: (params: ValueGetterParams) => any;
}
As you can see we have a mixture of different property types including primitive types as well as objects and functions.
Using the ColDef
interface we can create a new type that will enable us to type an object from which we can extract all the ColDef
keys from. We can do this using the Typescript Record utility type.
type ColDefObject = Record<(keyof ColDef), undefined>;
This type enforces that the object has every property from the ColDef
interface defined and set to undefined
. As we do not care about the actual values we give all the properties the undefined
type.
With this we can produce the following colDefProperties
object using autocompletion in our IDE thanks to Typescript.
const colDefProperties: ColDefObject = {
headerName: undefined,
columnGroupShow: undefined,
toolPanelClass: undefined,
valueGetter: undefined,
};
Then to produce the complete list of ColDef
property names we can then pass this to Object.keys
. We cast back to (keyof ColDef)[]
as otherwise ALL_PROPERTIES
will be typed as string[]
which is not so helpful to us.
const ALL_PROPERTIES = Object.keys(colDefProperties) as (keyof ColDef)[];
Conclusion
A bit of time spent finding the correct Typescript type can avoid the need to manually maintain code and ensure consistency without the need for complex code generators.
Top comments (2)
Maybe if you are using zod, you can just get the keys() and the options of your schema and you would be adding runtime validation as well.
Good idea, Thank you for sharing.
Apparently there is no clean way to get the keys directly from a type. This is not clean but at least ensures type safety.