Dynamic Type Inference from Object Paths in TypeScript
This challenge focuses on building a utility function that dynamically infers the TypeScript type of a property located at a given path within a deeply nested object. This is useful for scenarios where you need to access and type-check properties without knowing their exact structure at compile time, enabling more flexible and dynamic code. The function should traverse the object based on the provided path and return the inferred type.
Problem Description
You are tasked with implementing a function called getTypeByPath that takes two arguments: an object (obj) and a path (path). The path is an array of strings representing the sequence of property names to traverse within the object. The function should return the TypeScript type of the property at the end of the path.
What needs to be achieved:
- The function should traverse the object based on the provided path.
- It should return the TypeScript type of the property at the end of the path.
- If the path is invalid (e.g., a property doesn't exist), the function should return
unknown.
Key Requirements:
- The function must handle nested objects correctly.
- The function must return the correct TypeScript type, not just
any. - The function should be robust and handle invalid paths gracefully.
Expected Behavior:
The function should return the type of the property at the specified path. If any property in the path does not exist, it should return unknown.
Edge Cases to Consider:
- Empty path: Should return
unknown. - Path with a single property: Should return the type of that property.
- Path with multiple nested properties: Should return the type of the final property.
- Object with null or undefined values at intermediate steps in the path.
- Path leading to a primitive type (string, number, boolean, etc.).
- Path leading to an array.
- Path leading to a function.
Examples
Example 1:
Input:
obj: {
user: {
profile: {
name: string,
age: number
}
}
}
path: ['user', 'profile', 'name']
Output: string
Explanation: The path ['user', 'profile', 'name'] leads to the property 'name' within the nested object, which has the type 'string'.
Example 2:
Input:
obj: {
data: {
items: [
{ id: number, value: string }
]
}
}
path: ['data', 'items', 0, 'value']
Output: string
Explanation: The path ['data', 'items', 0, 'value'] leads to the 'value' property of the first element (index 0) in the 'items' array, which has the type 'string'.
Example 3:
Input:
obj: {
settings: {
theme: 'dark'
}
}
path: ['settings', 'unknownProperty']
Output: unknown
Explanation: The property 'unknownProperty' does not exist in the 'settings' object, so the function returns 'unknown'.
Example 4:
Input:
obj: {}
path: ['a', 'b']
Output: unknown
Explanation: The object is empty, so the path is invalid and the function returns 'unknown'.
Constraints
- The object
objcan be of any valid TypeScript type. - The path
pathwill always be an array of strings. - The length of the path can be any non-negative integer.
- The function should handle potentially large, deeply nested objects without causing stack overflow errors (consider iterative approach).
- Performance: The function should complete within a reasonable time (e.g., less than 100ms) for typical object sizes.
Notes
- Consider using TypeScript's type inference capabilities to determine the type of the property at the end of the path.
- You may need to use type assertions or type guards to handle different types of properties.
- An iterative approach (using a
forloop) is generally preferred over recursion to avoid potential stack overflow errors with deeply nested objects. - The return type should be
unknownwhen the path is invalid or the property doesn't exist. Avoid returninganyas it defeats the purpose of type safety.