Given an interface like this:
interface Theme {
color: {
red: string;
blue: string;
};
fontFamily: {
heavy: string;
light: string;
};
}
I would like to create a type that represents a valid property path in that interface.
i.e. something that would prevent compilation if I wrote an invalid path.
type ValidThemePathString = <insert solution here>;
const path1: ValidThemePathString = 'color.red'; // good
const path2: ValidThemePathString = 'color.green'; // compiler error
const path3: ValidThemePathString = 'fontFamily.red'; // compiler error
If this is not possible to achieve as a string through template literals, a tuple would be just as good
const path1: ValidThemePathTuple = ['color','red']; // good
const path2: ValidThemePathTuple = ['color', 'green']; // compiler error
const path3: ValidThemePathTuple = ['fontFamily', 'red']; // compiler error
Especially if the solution can provide hints to an IDE about what the next valid path part could be based on what has come previously in the tuple (narrowing down the options)
I only need this to work 2 levels deep if that helps
The best I've been able to come up with so far is a helper like this:
const path = <LevelOne extends keyof Theme, LevelTwo extends keyof Theme[LevelOne]>(
levelOne: LevelOne,
levelTwo: LevelTwo,
) => [levelOne, levelTwo];
const path1: ValidThemePathTuple = path('color','red'); // good
const path2: ValidThemePathTuple = path('color', 'green'); // compiler error
const path3: ValidThemePathTuple = path('fontFamily', 'red'); // compiler error
It feels like this should be possible without a helper, but I just haven't been able to make it work
Thanks in advance for any ideas
EDIT: If you're scanning this, the following link in a comment from @jcalz below is what you're looking for: here