Reputation: 171
Figured it out!
Initially, I was trying to import my module like this:
const qml = require('quill-marking-logic')
const { checkSentenceCombining, checkSentenceFragment, checkDiagnosticQuestion, checkFillInTheBlankQuestion, ConceptResult } = qml
because I got a TS2307: Cannot find module 'quill-marking-logic'
error when I tried to use
import { checkSentenceCombining, checkSentenceFragment, checkDiagnosticQuestion, checkFillInTheBlankQuestion, ConceptResult } from 'quill-marking-logic'
This was because I was using "module": "es6"
in my importing app's tsconfig, which by default sets the moduleResolution
option to Classic
. By explicitly setting it to node
, I was able to use the import
syntax and get my interfaces!
Original post
I've built a node module using Typescript that I am using as a dependency in another app. I have a couple of interfaces in the module that I am trying to export from the its entry point so that I can use them in my other app, but they are erased after compilation. I understand that this is part of Typescript's design, because the interfaces are used for runtime analysis, but I'm wondering if there's a way to get around it so I don't have to define them again in my other app and have to maintain the same code in two places. I'm using rollup as my bundler.
This is what the .d.ts version of my entry point looks like:
export { checkSentenceCombining } from './libs/graders/sentence_combining';
export { checkDiagnosticQuestion } from './libs/graders/diagnostic_question';
export { checkSentenceFragment } from './libs/graders/sentence_fragment';
export { checkFillInTheBlankQuestion } from './libs/graders/fill_in_the_blank';
export { Response, PartialResponse, ConceptResult, FocusPoint, IncorrectSequence, FeedbackObject, GradingObject, WordCountChange } from './interfaces/index';
That last line of exports is where the interfaces should be coming through.
Here is my tsconfig:
{
"compilerOptions": {
"target": "es5",
"module": "CommonJS",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"sourceMap": false,
"noImplicitAny": false,
"lib": [
"dom",
"es7"
],
"typeRoots": [
"node_modules/@types/"
],
"declaration": true
}
}
Here is my tsconfig for the app I'm trying to import this in to:
{
"compilerOptions": {
"outDir": "./dist/", // path to output directory
"sourceMap": true, // allow sourcemap support
"strictNullChecks": true, // enable strict null checks as a best practice
"module": "es6", // specifiy module code generation
"jsx": "react", // use typescript to transpile jsx to js
"target": "es6", // specify ECMAScript target version
"allowJs": true, // allow a partial TypeScript and JavaScript codebase
"lib": ["ES2017", "DOM"], //
"allowSyntheticDefaultImports": true // Allow import React from 'react'
}
}
And I'm pointing to the generated .d.ts file in the "typings" key in my package.json.
Upvotes: 17
Views: 26575
Reputation: 44377
Add this to your tsconfig.json
file:
"allowJs": false,
"declaration": true,
Add this to your package.json
file:
"types": "dist/index.d.ts",
Also, don't forget to put your d.ts
files in a separate folder that you publish in your package.json
, or they will be missed. For example, putting them in a typings
folder beside the src
folder and add this to package.json
"files": [
"/dist",
"/typings"
],
For further details: https://medium.com/cameron-nokes/the-30-second-guide-to-publishing-a-typescript-package-to-npm-89d93ff7bccd
Upvotes: 1
Reputation: 12804
Yes, this is possible. To do this, export
the interfaces you'd like to make available to consumers of your module in your module's entry point file:
// in entry.ts
import { MyInterface1 } from '/path/to/interface/one';
import { MyInterface2 } from '/path/to/interface/two';
export { MyInterface1, MyInterface2 };
Then, in your code that uses this module, you can do:
import { MyInterface1, MyInterface2 } from 'my-module`;
In order for this to work, you'll need to make sure the declaration
compiler option is set to true
in your module - this causes the compiler to output a .d.ts
file that contains your module's typing information.
The last piece of the puzzle is to include a types
property in your module's "package.json" that points to this .d.ts
file:
{
"name": "my-module",
"main": "./entry.js",
"types": "./my-module.d.ts"
}
For more information about preparing a module for publishing: https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html
Upvotes: 10
Reputation: 249506
You should use the --declaration
option to generate d.ts
files for your module. The definition file will contain exported interfaces and you can use them in your application.
You should include the definition file with your module in a location typescript looks for definitions in:
Similarly a non-relative import will follow the Node.js resolution logic, first looking up a file, then looking up an applicable folder. So import { b } from "moduleB" in source file /root/src/moduleA.ts would result in the following lookups:
/root/src/node_modules/moduleB.ts /root/src/node_modules/moduleB.tsx /root/src/node_modules/moduleB.d.ts /root/src/node_modules/moduleB/package.json (if it specifies a "types" property) /root/src/node_modules/moduleB/index.ts /root/src/node_modules/moduleB/index.tsx /root/src/node_modules/moduleB/index.d.ts
Upvotes: 1