1

I have a typescript interface like so:

interface DataSku {
  fields: {
    sku: string;
  }
}

interface PostProduct {
  fields: {
    title: string;
    skus: DataSku[];
  }
}

Now I want to extend the Product interface so that each object in the skus array has an extra field. So I tried this:

interface Sku extends DataSku {
  stripe: Stripe.skus.ISku;
}

interface Product extends PostProduct {
  fields: PostProduct['fields'] & {
    skus: Sku[];
  }
}

In my code, I try a loop like so:

(product as Product).fields.skus.forEach(sku => console.log(sku.stripe));

This throws the following Typescript error:

Property 'stripe' does not exist on type 'DataSku'.

Have I extended the interface wrong? The console does output my stripe object as expected, so it's just Typescript that isn't happy with the definition.

1 Answer 1

6

A much more elegant (and working) approach for this would be to use generics.

Update PostProduct with a generic parameter that's used for the type of fields.skus:

interface PostProduct<T extends DataSku = DataSku> {
  fields: {
    title: string;
    skus: T[];
  }
}

T extends DataSku means the type has to be a subtype of DataSku. And we can even set a default value for T so PostProduct can also be used without having to specify the generic parameter.

Now, for Product we just have to pass Sku as generic parameter:

interface Product extends PostProduct<Sku> {}

Playground


That said, if you want to it without generics and without modifying DataSku and PostProduct. You could do this:

interface Product extends PostProduct {
  fields: Omit<PostProduct['fields'], 'skus'> & {
    skus: Sku[];
  }
}

Playground

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

3 Comments

Yes I see what you're saying. Is this the only way to achieve this though? Ideally I don't want to mess around with my original DataSku and PostProduct interfaces.
It's definitely possible, just not as elegant... See the updated answer.
Perfect! Both solutions worked. To keep my linter happy on your first solution I had to do this: type Product = PostProduct<Sku>; to avoid declaring an empty interface.

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.