Here you have:
const notifications = [
{ title: 'Slack', type: 'slack' },
{ title: 'Email', type: 'email' },
] as const;
const model = {
slack: {
payload: {
a: ''
}
},
email: {
payload: {
b: ''
}
}
} as const
type Model = typeof model;
type notifications = typeof notifications;
type MapPredicate<T> = T extends notifications[number] ? T['type'] extends keyof Model ? { readonly type: T['type'] } & Model[T['type']] : 1 : 2
// http://catchts.com/tuples#map
type Mapped<
Arr extends Array<unknown>,
Result extends Array<unknown> = []
> = Arr extends []
? []
: Arr extends [infer H]
? [...Result, MapPredicate<H>]
: Arr extends [infer Head, ...infer Tail]
? Mapped<[...Tail], [...Result, MapPredicate<Head>]>
: Readonly<Result>;
// https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type/50375286#50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
// https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<
U extends any ? (f: U) => void : never
>;
// https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;
// https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union#comment-94748994
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
// http://catchts.com/union-array
type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true
? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]>
: [T, ...A];
type Arr = notifications[number]
const mapping = <T extends typeof notifications>(data: T) => {
const result = data.map((n) => {
const { title, ...rest } = n;
return {
...rest,
...model[n.type]
}
})
/**
* AFAIK, it is impossible to omit this type casting
*/
return result as Mapped<UnionToArray<Arr>>
}
const result = mapping(notifications)
type Return = [
{ type: 'slack', payload: { a: string } },
{ type: 'email', payload: { b: string } },
]
Because, JS arrays are mutable, I'm not sure it is possible to type result
as an array with exactly two elements.