6

I have a file named service.ts which exposes the following code:

export interface SomeInterface {
  keyOne: string;
}

export class TestService<T = SomeInterface> {
  property: T;
}

In index.ts file I am using the service:

import { TestService } from './service';

const service = new TestService();
service.property.keyOne

I also created index.d.ts file which declare the same interface SomeInterface with more keys:

export interface SomeInterface {
  keyTwo: number;
}

The problem is that service.property only "knows" the keyOne property. How can I tell typescript to merge both of them?

https://stackblitz.com/edit/typescript-cp8zmv

undefined
  • 6,366
  • 12
  • 46
  • 90

2 Answers2

11

If I understand you correctly (your comment in @chris p bacon's answer), you want to augment a module type definition from a library. The link to declaration merging in TypeScript docs is already a good catch. There are some good answers out there dealing with third party lib type extensions: here and here.

For your example, if we want to augment a library module type definition for some reason (let's say vendor-lib.d.ts instead of your index.d.ts to make it clearer), we can do that via Module Augmentation:

vendor-lib.d.ts:

export interface SomeInterface {
  keyTwo: number
}

service.ts

// here for simplicity a relative import
import { SomeInterface } from "./vendor-lib"

// Augment above imported module. 
// Important: this file must be a module (not a script) via `import/export`.
// Then augmentation will work. Otherwise compiler expects a module declaration.
declare module "./vendor-lib" {
  interface SomeInterface {
    keyOne: string
  }
}

index.ts:

const service = new TestService(); service.property = {...};
service.property.keyOne // works
service.property.keyTwo // works

StackBlitz

ford04
  • 66,267
  • 20
  • 199
  • 171
3

You'd extend the interface and give it another name

export interface SomeInterface {
  keyOne: string;
}

export interface SomeExtendingInterface extends SomeInterface {
  keyTwo: number;
}

Or merge them to a type that has both properties

interface Foo {
    foo: string;
}

interface Bar {
    bar: string;
}

type Baz = Foo & Bar;

const foo: Baz = {
    foo: '',
    bar: ''
};
baao
  • 71,625
  • 17
  • 143
  • 203
  • It should work as I did according to the docs https://www.typescriptlang.org/docs/handbook/declaration-merging.html – undefined Aug 11 '19 at 20:59
  • No matter if it works or not, why should two different interfaces ever have the same name? Don’t you think that’s confusing in a few months when maintaining your code? But do what you prefer... – baao Aug 11 '19 at 21:03
  • 2
    The purpose is for users that use a library to be able to extend the interface globally. – undefined Aug 11 '19 at 21:05
  • 1
    @undefined Honestly, you should add that in your question as context, otherwise, it doesn't really make that much sense. – ford04 Aug 12 '19 at 10:58