Joseph Zabinski
Joseph Zabinski

Reputation: 850

Typescript: how do I build an interface containing both an object and a string index type?

I need to describe an interface where:

  1. A property with a 'billingAddress' key has a value of an Object with specific properties, and
  2. Properties with any other key have a value of a string.

I tried this:

interface DoesNotWork {
  [key: string]: string;
  billingAddress?: {
    foo: string;
  }
}

Typescript complains that Property 'billingAddress' of type '{ foo: string; } | undefined' is not assignable to 'string' index type

Fair enough: when DoesNotWork.billingAddress is defined, Typescript won't know whether it should be assigned a string, an object or undefined.

How do I describe the interface in a way that Typescript will understand?

Upvotes: 1

Views: 1001

Answers (2)

RoundedHouse
RoundedHouse

Reputation: 81

With using Index Signatures because the exact propertyName is not known at compile-time, but the general structure of the data is known, creating a separate interface, to accommodate this data, and afterward composing the Interfaces to achieve the desired object structure, should help solve your problem OR extend using union types to increase the flexibility of values which are expected

This is because in trying to define different fields in the same interface, then you must cater for the option, of different (values) for the other fields aside from the Index Signature field

demo code

//composition approach(1)
interface BillingAddress{
  foo: string;
}

interface DoesNotWork{
  [key: string]: string;
}

interface ComposedInterface{
  indexSignature: DoesNotWork,
  billingAddress? : BillingAddress,
}


//extending value fields using unions approach(2)
interface BillingAddress{
  foo: string;
}

interface DoesNotWork{
   [key: string]: string | BillingAddress;
   billingAddress: BillingAddress;
}

Upvotes: 0

Ali Habibzadeh
Ali Habibzadeh

Reputation: 11558

Use discriminated union so you can mix and match.

interface DoesNotWork {
  billingAddress?: {
    foo: string;
  };
}

const foo: DoesNotWork | { [key: string]: string } = {
  billingAddress: { foo: "value" },
  key: "value"
};

Upvotes: 1

Related Questions