JavaScript Development Space

Typing Object Properties as Strings in TypeScript

When working with functions or methods that accept an object and its property as a string, it can be challenging to maintain type safety as objects evolve. Here's how to create a utility type in TypeScript to ensure that property names are type-checked at compile time.

Problem

Consider this function call:

ts
1 updateDate(user, "date");

If the property changes (user.date → user.birthday), the compiler won’t catch the mismatch, leading to potential bugs during runtime.

Solution

We can use a custom utility type, ValidPath, to ensure type safety for object properties.

Step 1: Define the Type

The ValidPath type recursively generates string representations of all object keys, including nested properties:

ts
1 type PropertiesOnly<T> = Pick<
2 T,
3 {
4 [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K;
5 }[keyof T]
6 >;
7
8 type IsArray<T> = T extends (infer U)[] ? true : false;
9
10 export type ValidPath<T, Prefix extends string = ''> = T extends object
11 ? {
12 [K in keyof PropertiesOnly<T> & (string | number)]: IsArray<T[K]> extends true
13 ? never
14 : `${K}` | `${K}.${ValidPath<T[K], `${Prefix}${K}.`>}`;
15 }[keyof PropertiesOnly<T> & string]
16 : never;

Explanation:

  • Exclude Functions: PropertiesOnly filters out methods from the object.
  • Handle Arrays: IsArray excludes arrays from the recursion to avoid complex union types.
  • Recursive Type: The ValidPath type generates all possible key paths in the object, including nested properties.

Step 2: Create a Utility Function

The validatePath function ensures that only valid property paths are accepted.

ts
1 export function validatePath<T>(path: ValidPath<T>): ValidPath<T> {
2 return path; // All checks occur during compilation
3 }

Example Usage

Given this object:

ts
1 const data = {
2 user: {
3 name: "Alice",
4 details: { age: 30 },
5 },
6 };

You can validate property paths like this:

ts
1 validatePath<typeof data>("user.name"); // Valid
2 validatePath<typeof data>("user.details.age"); // Valid
3 validatePath<typeof data>("user.invalid"); // Error

Additional Example

For more advanced scenarios:

ts
1 function floatHandler<T>(
2 value: string,
3 row: any,
4 property: ValidPath<T>,
5 propertyRaw: string
6 ) {
7 // Your code here
8 }
9
10 floatHandler<typeof data["user"]>(
11 "42",
12 data.user,
13 "details.age",
14 "age"
15 );

This approach ensures type safety and prevents runtime errors caused by invalid property names.

JavaScript Development Space

© 2025 JavaScript Development Space - Master JS and NodeJS. All rights reserved.