dx_over_dt
dx_over_dt

Reputation: 14328

How to extend/overwrite an exported type in Typescript

I'm using @fortawesome and @fortawesome/react-fontawesome. @fortawesome/fontawesome-common-types defines IconDefinition as

export interface IconLookup {
  prefix: IconPrefix;
  // IconName is defined in the code that will be generated at build time and bundled with this file.
  iconName: IconName;
}

export interface IconDefinition extends IconLookup {
  icon: any[];
}

export type IconProp = IconLookup | /*...*/;

export type IconName = /* a lot of concatenated strings */;

@fortawesome/react-fontawesome defines

export function FontAwesomeIcon(props: Props): JSX.Element;
export interface Props {
    icon: IconProp;
    /* ... */
}

I wanted to add my own SVGs and use them in the icon property of <FontAwesomeIcon />.

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

type CustomIconName = 'myCustomIcon' /* | ... */;
interface CustomIconDefinition extends Pick<IconDefinition, Exclude<keyof IconDefinition, 'iconName'>> {
    prefix: 'fab',
    iconName: CustomIconName;
}

const MyCustomIcon: CustomIconDefinition = /* ... */;

export default function() {
    let icon: any = MyCustomIcon;
    return <FontAwesomeIcon icon={icon} />;
}

This works so long as icon is of type any.

I'd like to remove CustomIconDefinition and overwrite or extend the IconName definition to include MyCustomIcon.

I thought to do something like:

import { IconName as VanillaIconName } from '@fortawesome/fontawesome-common-types';
declare module "@fortawesome/fontawesome-common-types" {
    export type IconName = VanillaIconName | MyCustomIcon;
}

This results in Duplicate identifier 'IconName'. index.d.ts(16,13): 'IconName' was also declared here. I suspect even if I get around this, it's going to have an error because IconName = VanillaIconName introduces a circular definition.

I also thought to overwrite the FontAwesomeIcon definition, but I didn't get far on that one because icon: IconProp is several types deep. I suppose I could create my own CustomFontAwesomeIcon extends FontAwesomeIcon, but this isn't ideal.

Upvotes: 7

Views: 4714

Answers (1)

dx_over_dt
dx_over_dt

Reputation: 14328

It occurred to me 2.5 years later that while type augmentation declaration merging isn't possible, interface declaration merging is.

I haven't tested this code yet, as I don't even remember which project I needed this for, but I should™ be able to augment Props of @fortawesome/react-fontawesome.

import { IconProp } from '@fortawesome/fontawesome-common-types';

declare module '@fortaweome/react-fontawesome' {
  export interface Props {
    icon: CustomIconProp;
  }
}

type CustomIconName = 'myCustomIcon' /* | ... */;

interface CustomIconProp extends Omit<IconProp, 'iconName'> {
  iconName: CustomIconName;
}

This doesn't solve the question posed in the title, but does solve the problem I had.


Note: there is an uber-hacky way to solve the problem posed by the title, by overwriting the package's declaration file using patch-package. Regrettably, I've taken this approach before for a dependency version I was bound to, but which future versions fixed the type I needed.

Upvotes: 3

Related Questions