Advertisements
I want to perform type checking on nested properties in an interface, and have the following code
type PathImpl<T, K extends keyof T> = K extends string
? T[K] extends Record<string, any>
? `${K}.${Path<T[K]>}` | K
: K
: never;
export type Path<T> = PathImpl<T, keyof T>;
export interface User {
name: string;
age: number;
dimensions: {
height: number;
weight: number;
}
address?: {
street: string;
city: string;
state: string;
};
}
const validPath: Path<User> = 'name'; // Works fine
const alsoValidPath: Path<User> = 'address'; // Works fine
const nestedValidPath: Path<User> = 'dimensions.height'; // Yay, works fine!
const doesntWorkPath: Path<User> = 'address.street'; // Doesn't work because address is optional
Is there a way to design the Path type to also allow ‘address.street’ to be valid, although address is optional?
>Solution :
You could use the Required
utility type in Path
to mark all properties on the input type as required:
export type Path<T> = PathImpl<Required<T>, keyof T>;
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^
Then your final assignment with "address.street"
is valid because the type of Path<User>
is:
| "name"
| "age"
| "dimensions"
| "address"
| "dimensions.height"
| "dimensions.weight"
| "address.street"
| "address.city"
| "address.state"