Home > Enterprise >  Using external type definitions with some types not exported
Using external type definitions with some types not exported

Time:10-05

I am using typescript in ReactJs together with Leaflet library (for displaying world maps). I needed to provide the typescript definitions for the library, but they already exist, so I installed them as a node module under @types.

In some place the library requires type of:

(feature: Feature<Geometry, any>, layer: Layer) => void

so I thought it will not be a problem, I created such function:

import L from 'leaflet';
...
function f(feature: L.Feature<L.Geometry, any>, layer: L.Layer){}

the Layer type works without any problems, but for Feature or Geometry I get:

Namespace '"/.../node_modules/@types/leaflet/index"' has no exported member 'Feature'

so I checked what is inside the type definition file:

export as namespace L;
import * as geojson from 'geojson';
...
onEachFeature?(feature: geojson.Feature<geojson.GeometryObject, P>, layer: Layer): void;

it's hidden under geojson, but when I try to access it, like L.geojson.Feature I get has no exported member named 'geojson', so I changed my own code to:

import L from 'leaflet';
import * as geojson from 'geojson';
...
(feature: geojson.Feature<geojson.Geometry, any>, layer: L.Layer

and it works but I am confused, because I thought you don't import type definitions, but only your code and the types living in x.d.ts will be imported for you (this is how the type L.Layer works). However, there is no geojson module in the main node_modules, it's nested under @types module. Am I doing something wrong here?

CodePudding user response:

I thought you don't import type definitions

Actually we do, it is just that TypeScript does it automatically for us with the actual import:

import L from 'leaflet'; // Bundler will import the Leaflet library,
// while TypeScript will look for types in the library, or in "@types"

There are several tsconfig modules options to configure this automatic behaviour, like types, typeRoots (by default the @types folder) and paths (for aliases).

As you figured out, it is also possible to import just types. You can use import type to make it explicit (and to hint the bundler that there is no code to actually import):

import type * as geojson from 'geojson';

It is also possible to access non exported types by deep access:

type TonEachFeature = L.GeoJSONOptions["onEachFeature"];
//   ^? ((feature: geojson.Feature<geojson.GeometryObject, any>, layer: Layer): void) | undefined

// You can type the function and let TS infer the argument types automatically
const myOnEachFeature: TonEachFeature = (feature, layer) => {
    feature
    // ^? geojson.Feature<geojson.GeometryObject, any>
    layer
    // ^? L.Layer
}

And using utility types:

function myOnEachFeature2(feature: Parameters<NonNullable<TonEachFeature>>[0], layer: L.Layer) {
    feature
    // ^? geojson.Feature<geojson.GeometryObject, any>
}

Playground Link

  • Related