Search code examples
typescripttypescript-typingsintersectionrecursive-type

Deep intersection type of two object types in TS


Is there anything like "&" for deep/recursive intersection of two object types? Meaning only allow accessing properties that are present in all of the merged types.

Here is what I want to do:

// Defined in its own file
const en = {
  "welcome": {
    "hello": "Hello",
    "world": "World"
  }
}

// Defined in its own file
const hu = {
  "welcome": {
    "hello": "Helló világ"
  }
}

// How to write this type?
// Should be deep intersection,
// meaning only mutually available properties allowed
let locale: typeof en & typeof hu

//  Using a Webpack resolve.alias
//  resolve.alias.locales = path.resolve(
//    __dirname,
//    `locales/${process.env.NEXT_PUBLIC_LOCALE}`
//  )
//@ts-ignore
locale = {}

locale.welcome.world // This should NOT be available
locale.welcome.hello // This SHOULD be availeble

Solution

  • You need to use union to get the common of two. I understand it is linguistically (Venn diagram wise) opposite, but it works that way. (I highly recommend reading the comments and links on this answer by @jcalz for a better explanation as on why & and | makes sense when you look them from type assignment point of view.)

    TS Playground

    const en = {
      welcome: {
        hello: "Hello",
        world: "World",
      },
    };
    
    const hu = {
      welcome: {
        hello: "Helló világ",
      },
    };
    
    let locale = {} as typeof en | typeof hu;
    
    // Error: Property 'world' does not exist on type '{ hello: string; world: string; } | { hello: string; }'.
    locale.welcome.world; // This should NOT be available
    
    // Works
    locale.welcome.hello; // This SHOULD be availeble