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>
}