Home > Net >  TypeScript: How to import a type only if it is defined in the other file
TypeScript: How to import a type only if it is defined in the other file

Time:11-03

I have a set of files (web components). Some of those files define an optional type (it is in fact an interface with type definitions of the event.detail property for every custom event the component triggers, but let's for simplicity call it OptionalType).

There is another set of files, which are generated automatically. Each of those file corresponds to one web component and provides Preact types for that component.

I am looking for a way to conditionally define some of the types in the second, generated set of files, based on whether the original web component file exported OptionalType or not.

To give you a simplified example, this code could be in a component file:

export interface OptionalType {
  name: string
}

I figured initially that I could import * as all and then to check if all['OptionalType'] (or typeof all['OptionalType'], or all.OptionalType) exists. This is how I intend to use that type in the generated file:

import * as all from "./my-component"

type OptionalType = typeof all extends {
  OptionalType: unknown
}
  ? typeof all["OptionalType"]
  : never

// Do something with OptionalType

Unfortunately, this doesn't work. In JS all would be an object holding all the exports as properties. But in TS it is what seems to be a namespace. And I have no idea how to deal with those.

If I try to define those types inline for testing purposes, everything works as expected:

interface All {
  OptionalType: {
    name: string
  }
}

type OptionalTypeInline = All extends {
  OptionalType: unknown
}
  ? All["OptionalType"]
  : never

// Do something with OptionalType

You can also check or tweak this example demo.

CodePudding user response:

So I don't know if this is what you want, but you can use ambient modules in combination with declaration merging to check if a type exists in a module:

import * as all from "./types";

// This declares that there is an interface 'OptionalType' in "./types"
// If OptionalType does NOT exist in "./types" it will be declared as {}
// If OptionalType does exist in "./types" our declaration will be merged and it will have the type { name: string; }
declare module "./types" {
  interface OptionalType {
  }
}

// Here we check if it has the name attribute to know wether it was the declaration by us or the merged declaration.
type OptionalTypeConditional = all.OptionalType extends {
  name: string
} ? all.OptionalType
  : never;

In my testing this only works, if there is at least one other type in ./types. Otherwise it is not recognized as a module if it is completely empty.

CodePudding user response:

import { OptionalType } from './my-component';

// ...

You can import exported members of a module by encasing them in curly braces.

  • Related