Just an example function:
// Merges objects | arrays
function merge(...values) {
  return Object.assign(
    {},
    ...values.map((value) =>
      Array.isArray(value)
        ? Object.fromEntries(value.map((val) => [val, null]))
        : value,
    ),
  )
}
merge({k1: 1}, {k2: 2}) // {k1: 1, k2: 2} - 
merge({k1: 1}, ['k2'])   // {k1: 1, k2: null} - 
I'm trying to figure out how to write types for the function and keep the structure of the result
// Types definition
export type MixType<T> = T extends string[]
  ? { [K in T[number]]: null }
  : { [K in Extract<keyof T, string>]: T[K] }
type Test1 = MixType<{k1: 1}> // Type is: {k1: 1} - 
type Test2 = MixType<['k1']>   // Type is: {k1: null} - 
// Bind types into the function
function merge<V1>(v: V1): MixType<V1>
function merge<V1, V2>(v1: V1, v2: V2): MixType<V1> & MixType<V2>
function merge(...values) { // ... }
const t1 = merge({k1: 1}, {k2: 2}) // typeof t1: {k1: number} & {k2: number} - 
const t2 = merge({k1: 1}, ['k2']) // typeof t2: {k1: number} & {[x:string]: null} - ♂️
const t3 = merge(['k1']) // typeof t3: {[x: string]: null} - ♂️
How to make the typescript keep the resulting structure with arrays? How I can understand T[number] and Extract<keyof T, string> are both produce a union. So it has to be the same {[K in <Union>} in both cases. But for arrays ts drops result structure.
So there are questions:
- how to make 
merge({k1: 1}, ['k2'])to get type of{k1: number} & {k2: null} - how to make it even better: 
merge({k1: 1}, ['k2'])to get type of{k1: 1} & {k2: null} 
Consolidated answer
based on @TadhgMcDonald-Jensen response and comments from @TitianCernicova-Dragomir
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I,
) => void
  ? I
  : never
type MixType<T> = T extends readonly string[]
  ? { [K in T[number]]: null }
  : { [K in keyof T]: T[K] }
function merge<
  Vs extends Array<S[] | Record<S, V>>,
  S extends string,
  V extends string | number | boolean | object,
>(...values: Vs): UnionToIntersection<MixType<Vs[number]>> {
  return Object.assign(
    {},
    ...values.map((value) =>
      Array.isArray(value)
        ? Object.fromEntries(value.map((val) => [val, null]))
        : value,
    ),
  )
}
const t1 = merge({ k1: 1 }, { k2: '2' })
// typeof t1: { k1: 1} & {k2: '2'} - 
const t2 = merge({ k1: true }, ['k2'])
// typeof t2: { k2: null} & {k1: true} -