Suppose We have an interface A that has two properties:
interface A {
a: string;
b: {
c: string;
};
}
type Key = ???????
const test: Key[] = ['a', 'b', 'c'];
so how can we define and make it work ?
Playground link here.
CodePudding user response:
You can have an util type that get's all the keys of the object recursively, something like:
interface A {
a: string;
b: {
c: string;
};
}
// create a type that gets keys recursively
type RecursiveKeyOf1<TObj extends object> = {
[TKey in keyof TObj & (string | number)]: TObj[TKey] extends object
? `${TKey}` | `${RecursiveKeyOf<TObj[TKey]>}`
: `${TKey}`;
}[keyof TObj & (string | number)];
// create a type for the object's keys
type ObjectKeys = RecursiveKeyOf1<A> // "a" | "b" | "c"
const keys: ObjectKeys[] = ["a", "b", "c"]
CodePudding user response:
You can define a conditional type that applies the keyof
type operator to all objects and recursively in those objects' properties. For example:
type NestedKeyof<T> = T extends object ?
{ [K in keyof T]: K | NestedKeyof<T[K]> }[keyof T]
: never
This works for your example code:
interface A {
a: string;
b: {
c: string;
};
}
type Key = NestedKeyof<A>;
// type Key = "a" | "b" | "c"
const test: Key[] = ['a', 'b', 'c'];
There are always caveats, so you should test this against your real use cases. For instance, if you write NestedKey<Tree>
with interface Tree { prop: Tree }
, you'll get a circularity warning (which makes sense).