If you pass the --allowJs flag as true in your tsconfig.json, it tells the TypeScript compiler to compile JavaScript files as well. Therefore, with this flag set to true, TypeScript will know about the modules you've defined in your JavaScript files, and you will not need to do any extra trickery with declaration files.
Therefore an example tsconfig.json file could look like this:
{
"compilerOptions": {
"allowJs": true
"module": "commonjs",
"noImplicitAny": true,
"target": "es6"
},
"exclude": [
"node_modules"
]
}
(https://www.typescriptlang.org/docs/handbook/compiler-options.html)
Of course, the config file would entirely depend on your project, but you would just add "allowJS": true as one of your "compilerOptions".
Note: This is available as of TypeScript 1.8
The relevant release notes are here:
https://www.typescriptlang.org/docs/release-notes/typescript-1.8.html#including-js-files-with---allowjs
-- EDIT --
In response to comments about requiring types along with your internal JS imports, I have come up with the following. However, if going to this much trouble to add types to your JavaScript modules, I would just suggest converting the file to TypeScript and typing all of your exports at the bare minimum (in fact, looking back at this edit, this seems really unnecessary unless it is absolutely impossible for whatever reason to convert your JS to TS). But anyway...
You would still pass "allowJs": true in your tsconfig.json, but you can create interfaces for the JavaScript modules you want, then type the imports in your TS file. The following provides an example, with the JS file and the TS file a little more fleshed out to show the possibilities:
Folder Structure
src
| - javascript
| | - jsToConsume.js
| - typescript
| | - client.ts
typings
| - typings.d.ts
tsconfig.json
jsToConsume.js
export const yourHair = (adjective) => {
return `Your hair is ${adjective}`;
}
export let jam = 'sweet';
export class AnotherClass {
constructor() {
this.foo = 'bar';
}
}
export default class Hungry {
constructor() {
this.hungry = true;
}
speak() {
return 'More cake please';
}
}
typings.d.ts
declare interface jsToConsumeModule {
yourHair: (adjective: string) => string;
jam: string;
AnotherClass: AnotherClassConstructor;
}
declare interface Hungry {
hungry: boolean;
speak: () => string;
}
declare interface HungryConstructor {
new (): Hungry;
}
declare interface AnotherClass {
foo: string;
}
declare interface AnotherClassConstructor {
new (): AnotherClass;
}
client.ts
import { yourHair as _yourHair_ } from './../javascript/jsToConsume';
const yourHair: (adjective: string) => string = _yourHair_;
import * as _jsToConsume_ from './../javascript/jsToConsume';
const jsToConsume: jsToConsumeModule = _jsToConsume_;
import _Hungry_ from './../javascript/jsToConsume';
const Hungry: HungryConstructor = _Hungry_;
So, when importing individual members and defaults from a module, just give each the desired type. Then you can give an interface for the public exports of the module when using import * as ....
NOTE
But you must have a really good reason why you don't want to just change your JS files into TS. Think for a moment that you want types for your files, and you have control of them as they are internal to your project, so that sounds like the exact use case for why TS exists. You can't control external modules, so that's why you build declaration files to create an interface for interacting with the library. If you are determined to add types to your JavaScript, then you can do that by making it TypeScript.