How can I use Typescript's declaration merging with an interface imported from a module?
Asked Answered
D

1

10

https://www.typescriptlang.org/docs/handbook/declaration-merging.html

The above link provides information on declaration merging with interfaces. I'd like to be able to do that with an interface that's got a generic component. I'm currently using Typescript 3.0.3.

This does what I want, but I don't understand why I can't do the same thing with declaration merging.

interface MyRouteComponentProps<P, C extends StaticContext = StaticContext> extends RouteComponentProps<P, C> {
    loadCandidateFromQueryParam: (candidateId: number) => void
}

class CandidateDetailContainer extends React.Component<MyRouteComponentProps<RouteMatchProps>, {}> {

    public componentWillMount() {
        this.props.loadCandidateFromQueryParam(Number(this.props.match.params.candidateId));
    }

Why doesn't this work?

interface RouteComponentProps<P, C extends StaticContext = StaticContext> {
    loadCandidateFromQueryParam: (candidateId: number) => void
}

class CandidateDetailContainer extends React.Component<RouteComponentProps<RouteMatchProps>, {}> {

It seems to completely override the entire definition for RouteComponentProps instead of merging them. I get errors related to P and C never being used (if the definitions were merged then I would expect those errors to go away because they're used in the primary definition). And then I get an error about "match" field not being there. Again, another field that was present in the original definition.

For reference here's the original definition that I'm trying to merge with.

export interface RouteComponentProps<P, C extends StaticContext = StaticContext> {
  history: H.History;
  location: H.Location;
  match: match<P>;
  staticContext: C | undefined;
}
Delanos answered 3/10, 2018 at 23:35 Comment(0)
A
14

In fact, in your second example, when I add the imports that I assume you must have:

import * as React from "react";
import { RouteComponentProps, StaticContext } from "react-router";

I get an error on the import of RouteComponentProps.

You can't merge content into an imported symbol (interface, namespace, etc.) by declaring a local symbol of the same name; either this will be an error or it will shadow the imported symbol. To merge definitions, they must be in the same scope. In the case of merging content into a symbol from a module, you can use a module augmentation to put your definition in the scope of the original module:

declare module "react-router" {
    interface RouteComponentProps<Params, C extends StaticContext = StaticContext> {
        loadCandidateFromQueryParam: (candidateId: number) => void
    }
}

(What makes this a module augmentation rather than a module declaration is that it is nested inside another module. Obscure, I know.) Note that the names of the type parameters have to be the same as in the original interface.

Accipitrine answered 4/10, 2018 at 0:46 Comment(2)
Argh that makes sense. I didn't think about the fact that the definitions are in modules.Delanos
Module Augmentation worked for me. I used this tutorial: javascript.plainenglish.io/…Odel

© 2022 - 2024 — McMap. All rights reserved.