LazioTibijczyk
LazioTibijczyk

Reputation: 1937

TypeScript assumes merged interfaces are of type never

I have created a type by merging some interfaces. For some reason TypeScript assumes my type is going to be of type never. Could you point out what I am doing wrong so that TypeScript does that?

type OrganizationAccount = Account & DetailedAccount & AccountSelection;

interface Account {
  name: string;
  created_on: string;
}

interface DetailedAccount {
  account_type: string;
  status: string;
}

interface AccountSelection {
  isSelected?: boolean;
}

My first API request returns just the Account properties (name and created_on). The user may get the details by clicking a button which then makes another request and after it receives extra properties spreads the account object adding DetailedAccount properties.

function getAccountDetails(account: OrganizationAccount) {
  setOrganizationAccounts((prevState: OrganizationAccount[]) => {
    return prevState.map(organizationAccount => {
      return organizationAccount.name === account.name
        ? {
          ...organizationAccount,
          ...accountDetailsRes,
          }
        : organizationAccount;
      });
    });
  });
}

Later I am checking whether account_type is present and based on that I display different results however, this is where TypeScript thinks my account variable is of type never.

function accountType(account: OrganizationAccount) {
  if (!('account_type' in account)) { // Here account === OrganizationAccount
    console.log('account', account);  // Here account === never

    return 'Loading...';
  }

  return account.account_type;
}

Upvotes: 1

Views: 525

Answers (2)

omarbenzi
omarbenzi

Reputation: 35

Try this:

type A = {
   mayVar1: string;
   mayVar2: boolean;
};

type B = {
    mayVar3: string;
    mayVar4: boolean;
};
type AandB = A & B;

Upvotes: 0

jabaa
jabaa

Reputation: 6745

if (!('account_type' in account)) { is always false in the TypeScript world because the property account_type is mandatory in OrganizationAccount.

You probably want to set OrganizationAccount to

type OrganizationAccount = (Account | Account & DetailedAccount) & AccountSelection;

I prefer

type OrganizationAccount = (Account | DetailedAccount) & AccountSelection;

interface Account {
  name: string;
  created_on: string;
}

interface DetailedAccount extends Account {
  account_type: string;
  status: string;
}

interface AccountSelection {
  isSelected?: boolean;
}

A working example:

type OrganizationAccount = (Account | Account & DetailedAccount) & AccountSelection;

interface Account {
  name: string;
  created_on: string;
}

interface DetailedAccount {
  account_type: string;
  status: string;
}

interface AccountSelection {
  isSelected?: boolean;
}

let a: OrganizationAccount = {
  name: 'a',
  created_on: 'string',
  isSelected: false
};
a.name = 'a';
a.created_on = 'string';
a.isSelected = true;

let b: OrganizationAccount = {
  name: 'b',
  created_on: 'string',
  isSelected: false,
  account_type: 'type',
  status: 'string'
};
b.name = 'b';
b.created_on = 'string';
b.isSelected = true;
b.account_type = 'type';
b.status = 'string';

function f(account: OrganizationAccount) {
  console.log(account.name);
  if ('account_type' in account) {
    console.log(account.account_type);
  }
}

f(a);
f(b);

TypeScript playground

Upvotes: 1

Related Questions