Home > Software design >  How can I merge types back in TypeScript without touching the original types
How can I merge types back in TypeScript without touching the original types

Time:02-03

Let's say I have the following types:

type A = {
    role: 'admin',
    force?: boolean
}

type B = {
    role: 'regular',
}

type Or = A | B;

I want to be able to do:

const or = {
    role: 'regular'
} as Or;

const {role, force} = or;

without getting the error:

Property 'force' does not exist on type 'Or'.(2339)

and without using Touching A and B

I tried to do:

type Merge<Obj> = {
    [k in keyof Obj]: Obj[k]
}

const {role, force} = or as Merge<Or>;

but same error

TypeScript Playground

CodePudding user response:

You cannot access properties of a union type that only some members of that union type have.

You're getting the same error, and for the same reason is this simplified code:

type A = {
    role: 'admin',
    force?: boolean
}

type B = {
    role: 'regular',
}

const obj: A | B =
    Math.random() > 0.5 ?
        { role: 'admin', force: true } :
        { role: 'regular' }

obj.force // type error

If you want that property, then you have to prove that it exists first which narrows your union to just the values that that property.

if ('force' in obj) obj.force // fine

See Playground


You can add the same thing to your playground in order to make typechecking pass.

const mergedOr = or as Merge<Or>;
const force = 'force' in or && or.force; // fine

See Playground


It's also worth mentioning that this type:

type Merge<Obj> = {
    [k in keyof Obj]: Obj[k]
}

Does nothing useful. It maps an object types keys to the value types for those keys, which will exactly the same as the input type. So whatever you think this is supposed to do, it's not doing that.

CodePudding user response:

There is absolutely no way to merge two generic types in typescript without knowing the structure of the types before hand. Typescript uses a static type system. It does not allow dynamic manipulation of types.

CodePudding user response:

use the Pick utility to achieve that and here is my solution you might consider helpful:

type A = {
    role: 'admin',
    force?: boolean
}

type B = {
    role: 'regular',
}
type Or = Pick<A & B, 'force' | 'role'>;
const or = {
    role: 'regular'
} as Or;

const {role, force} = or;
  • Related