I want to deeply remove all localization keys but inject its Record Type where the localization key was. The type transformation should also work for Arrays.
For example:
Given Type:
type Given = {
foo: string;
bar: number;
localizations: {
de: {
name: string;
email: string;
};
en: {
name: string;
email: string;
}
}
deep: Array<{
foo: string;
localizations: {
de: {
bar: string;
};
en: {
bar: string;
}
}
}>
}
Desired:
type Desired = {
foo: string;
bar: number;
name: string;
email: string;
deep: Array<{
foo: string;
bar: string;
}>
};
CodePudding user response:
You can use a mapped recursive type
type FlattenDeep<T, P extends PropertyKey> =
// we unwrap the contents of the key `P`, here "localization"
( T extends { [K in P]: unknown }
? T[P][keyof T[P]]
: unknown )
// we intersect it with the object Omitting that key
& {
[K in keyof T as K extends P ? never : K]: // the key `P` is filtered out
// the recursive bit
T[K] extends unknown[] ? FlattenDeep<T[K][0], P>[]
: T[K] extends {} ? FlattenDeep<T[K], P>
: T[K]
}
type Desired = FlattenDeep<Given, 'localizations'>;
type Desired = ({
name: string;
email: string;
} | {
name: string;
email: string;
}) & {
foo: string;
bar: number;
deep: FlattenDeep<{
foo: string;
localizations: {
de: {
bar: string;
};
en: {
bar: string;
};
};
}, "localizations">[];
}
Now if you don't like the way the resulting type looks like in tooltips you can force it to compute (the two types should be equivalent, modulo some TS quirks which I don't think apply here)
type Compute<T> = unknown & { [K in keyof T]: T[K] }
type KillUnion<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
? I : never
type FlattenDeep<T, P extends PropertyKey> = Compute<
( T extends { [K in P]: unknown }
? KillUnion<T[P][keyof T[P]]>
: unknown
) & {
[K in keyof T as K extends P ? never : K]:
T[K] extends unknown[] ? FlattenDeep<T[K][0], P>[]
: T[K] extends {} ? FlattenDeep<T[K], P>
: T[K]
}
>
type Desired = FlattenDeep<Given, 'localizations'>;
type Desired = {
name: string;
email: string;
foo: string;
bar: number;
deep: {
bar: string;
foo: string;
}[];
}