Declare Interfaces in typings file for JavaScript
Asked Answered
C

3

12

Project Info

I'm working on a JavaScript project that utilizes .d.ts files. This is a subsequent question to a question I previously asked, so you can view more information regarding the project here.

Problem

Although I can normally extract functions from the typings files I can't extract interfaces or namespaces that are either empty or consist purely of interfaces. I have temporarily fixed this problem by creating a const implementation for each interface and using @typeof ConstantImplementation in the comments. See Example below:

// Typings File
export namespace test {
    export interface ITest {
        foo: string;
        bar: number;
    }
    export const Test: ITest;
}

// JS File
if (undefined) var {Test: ITest} = require("globals.d.ts").test; 
// Above line shows `unused var error`

/* @type {typeof ITest} */
var x = {};
x.foo = "hello";
x.bar = 3;
// if I do `x.` intellisense should suggest `foo` and `bar` 

I was wondering if there is a better way to go around the problem, preferably one that doesn't throw an error (using eslint ignore line isn't a fix).

Clarification

This question is not about getting functionality from typings file. It's purely about making VSCode intellisense working with typings Interfaces. Here is an image to explain what it is that I want (the two lines inside the circle):

enter image description here

Cranford answered 27/6, 2018 at 10:16 Comment(0)
F
14

So I was able to solve the issue using JSDoc

test.d.ts

export namespace test {
    export interface ITest {
        foo: string;
        bar: number;
    }
}

test.js

/**
 * @type {import("./test").test.ITest}
 */

let x;

x.

And the intellisense works now

Working intellisense

Also one thing I found is that if you add jsconfig.json with

jsconfig.json

{
    "compilerOptions": {
        "checkJs": true
    }
}

Your intellisense improves further

Better intellisense

Update-1

As pointed out by @nickzoum, if you define the test.d.ts like below

export interface ITest {
    foo: string;
    bar: number;
}

export as namespace test;

Then you can also use below form in JS for intellisense

/** @typedef {import("./test").ITest} ITest */

/** @type {ITest} */
var x = {};
x.
Flemings answered 2/8, 2019 at 8:9 Comment(1)
It seems that you can still wrap your declarations in the namespaces you want. But you also have to use export namespace as randomName globally. So if you just add export namespace as randomName to your first example, you can do @typedef {import("./test")} JSDoc` and then /** @type {JSDoc.test.ITest} */Cranford
L
6

I'm thinking there may be a conceptual misunderstanding that underlies your problem here. It sounds like you want the interfaces to be available at runtime. Typescript interfaces are purely a compile-time concept. They do no compile to anything. They have no existence at runtime.

I took this part of your code and put it in a file named interf.d.ts:

export namespace Interfaces {
    export interface Interface {
        property: string;
    }
}

Then I created the file test.ts:

import { Interfaces } from "./interf";

const x: Interfaces.Interface = {
    property: "abc",
};

console.log(x);

I get no compilation error, and it executes just fine. As expected, the interface is exported. The const you export later is not needed to export the interface (and anyway it does not export an interface, it exports a const which is declared to conform to the interface, but the const is not the interface).

However, if you're trying to find something in the compiled JavaScript that corresponds to your interface, you won't find it, for the reason I gave above: it is a compile-time construct.

Laws answered 2/2, 2019 at 19:11 Comment(0)
S
3

I found something that is working without any extra configuration and with a simple usage, but you need to configure a tsconfig.json.

tsconfig.json

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "moduleResolution": "node",
    "baseUrl": "../",
    "noEmit": true,
    "target": "es5"
  }
}

test.d.ts

export = Test
export as namespace Test
declare namespace Test {
  interface ITest {
    foo: string;
    bar: number;
  }
}

test.js

/**
 * @type {Test.ITest}
 */
let x;
Surety answered 11/9, 2019 at 16:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.