1

Before going further, let me show you an example in JavaScript:

let a = 5
function fn() {
  console.log(a)
}
fn() // 5
a = 10
fn() // 10

The first function call logs the output 5, and the last call logs to 10.

In this sense, what I am thinking of TypeScript's interface behavior to merge. An example would be good to illustrate this:

Interfaces are merged:

interface Example {
  foo: string
}
interface Example {
  bar: string
}

So, this becomes:

interface Example {
  foo: string
  bar: string
}

Now, let me show you an example representing the thoughts:

interface Person {
  name: string
}

function myPersonFn() {
  interface Person {
    age: number
  }
  const inPerson: Person = {name: 'Bhojendra', age: 37}
  console.log(inPerson)
}

interface Person {
  address: string
}

const outPerson: Person = {name: 'Rauniyar', address: 'Kathmandu'}
console.log(outPerson)

This throws an error: (Good, the interface Person is function scoped.)

Type '{ name: string; age: number; }' is not assignable to type 'Person'.

  • Object literal may only specify known properties, and 'name' does not exist in type 'Person'.

Now, let's try extending it:

interface Person {
  name: string
}
function myPersonFn() {
  // type ScopedPerson = Person & {
  //   age: number
  // }
  interface ScopedPerson extends Person {
    age: number
  }
  const inPerson: ScopedPerson = {name: 'Bhojendra', age: 37}
  console.log(inPerson)
}
myPersonFn()
interface Person {
  address: string
}
const outPerson: Person = {name: 'Rauniyar', address: 'Kathmandu'}
console.log(outPerson)

This throws an error:

Property 'address' is missing in type '{ name: string; age: number; }' but required in type 'ScopedPerson'.

This is in fact typescript behavior. As it merges the interfaces the Person interface expects address to be in it.

But I could ask to TypeScript, can I ignore that?

Well, what if the function call at an end?

interface Person {
  address: string
}
myPersonFn()

Hmm, this makes us think that TypeScript is doing best thing for us not allowing to miss the address property.


Wait! What I am thinking for this is behave like JavaScript code as the a value logged in first code block. Mmm, generic something like to meet both behavior?

interface ScopedPerson<T, Optional> extends Person {

I don't know if this is even possible? You might have an idea, if you get my point?

What I want is, do not throw error, let it compile OK this line of code inside the function block:

const inPerson: ScopedPerson = {name: 'Bhojendra', age: 37}
console.log(inPerson)

Pretty well, I am not talking about optional address property:

interface Person {
  address?: string
}
4
  • Are you asking if code can ignore interface merges that come after that code? Commented Dec 18, 2022 at 21:28
  • Yes, exactly like that. Commented Dec 18, 2022 at 21:33
  • THis sounds a lot like an XY Problem. If you think need this, then you are probably trying to solve the wrong problem. Commented Dec 18, 2022 at 21:44
  • Yes, I think so. I might be overthinking. And it's not possible. Commented Dec 18, 2022 at 21:46

1 Answer 1

0

As it merges the interfaces the Person interface expects address to be in it. But I could ask to TypeScript, can I ignore that?

This is not possible. Type declarations completely are unordered. That's why this is fine:

type A = { a: B }
type B = number

But that also means that this:

interface Person { name: string }
function myPersonFn() {
  interface ScopedPerson extends Person { age: number }
  const inPerson: ScopedPerson = {name: 'Bhojendra', age: 37} // error
}
interface Person { address: string }

And this:

interface Person { name: string }
interface Person { address: string } // moved this before the function
function myPersonFn() {
  interface ScopedPerson extends Person { age: number }
  const inPerson: ScopedPerson = {name: 'Bhojendra', age: 37} // error
}

Are 100% identical in every way.

So there is no possible way for Typescript to know you want one piece if of an interface, but not another. They don't have different names or identifiers, and there's no record in the type itself that this was merged at all.


If you want to reason about sub sets of this type, then they need to be separate types.

For example:

// This is just a piece of a `Person`, so call it `PersonName`.
interface PersonName { name: string }

// Maybe create another type for only a persons address
interface PersonAddress { address: string }

// Then create a full person from its pieces
interface Person extends PersonName, PersonAddress {}

function myPersonFn() {
  interface ScopedPerson extends PersonName { age: number }
  const inPerson: ScopedPerson = {name: 'Bhojendra', age: 37} // fine
}


Here PersonName is just the name property, ScopedPerson adds the age since that's only used interally in that function, and Person extends PersonName and adds address.

See Playground

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for clarification that this is not possible. I was thinking of something generic would do that :) My intension was to accept both behavior (with and without address) inside that function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.