Unlocking Not Exported Type Definitions in TypeScript

Take a look at this TypeScript code snippet:


lib.ts

interface Person {
    name: string;
    age: number;
}

export default class PersonFactory {
    getPerson(): Person {
        return {
            name: "Alice",
            age: 30,
        }
    }
}

index.ts

import PersonFactory from "./lib";

export class Bar {
    person: any;

    constructor() {
        const factory = new PersonFactory();
        this.person = factory.getPerson();
    }

    calculateAgeDifferenceWithError(age: number): number {
        return age - this.person.name;
    }

    calculateAgeDifferenceWithTypingAndAutocomplete(age: number): number {
        const factory = new PersonFactory();
        return age - factory.getPerson().name;
    }
}

The issue lies in the "person" property of the "Bar" class. It's difficult to define the type of this variable as the "Person" interface from lib.ts.

In the "calculateAgeDifferenceWithError" method, there is a mistake - using a number "age" and a string "name" in an arithmetic operation, but neither the IDE nor the TypeScript compiler detect it because, in this context, the type of "this.person.name" is set to "any".

In the "calculateAgeDifferenceWithTypingAndAutocomplete" method, I simply use the "getPerson" method. The IDE and compiler recognize the type of the method result which is the "Person" interface with a "string" field for "name". This method triggers an error during compilation.


I encountered this problem when attempting to import a .d.ts file of a JavaScript library where exporting the required interface was not possible. Is there a way to define the valid type of the "person" property without repeatedly copying and pasting the "Person" interface whenever setting the type (and without inline type declarations, like { name: string, age: number })?

I do not intend to create instances of non-exported classes; I just need type checking and auto-completion features.


P.S. I attempted to use this:

person: Person

and received a compiler error: "error TS2304: Cannot find name 'Person'" (as expected)


P.S.S I also tried using the triple-slash directive:

///<reference path="./lib.ts" />

but unfortunately, that didn't work either.


Apologies for my limited English proficiency and thank you for your responses

Answer №1

New Information!

Discover the power of conditional types in TypeScript for an easier solution:

Type 'Human' is now defined as the return type from the 'getHuman' method in the 'HumanFactory':
type Human = ReturnType<HumanFactory['getHuman']>

Previous Solution for TypeScript versions before 2.8

If you're unable to modify the 'lib.ts' file, here's a workaround to determine the return type of the 'getHuman' function:

import HumanFactory from "./lib";

const dummyHuman = !true && new HumanFactory().getHuman();
type Human = typeof dummyHuman;

export class Foo {
  human: Human;

  // ...
}

The use of !true && prevents the execution of new HumanFactory().getHuman().

Answer №2

I've discovered the solution!

To resolve the issue, I created a file named human-interface.ts with the following code:

import HumanFactory from './lib';

const humanObject = new HumanFactory().getHuman();
type HumanType = typeof humanObject;

export default interface Human extends HumanType {}

By importing this interface in the main file, "HumanFactory" is not instantiated and type checking functions properly.

A big thanks for suggesting the use of typeof.

Answer №3

To make the Human object visible and usable in both the index.ts file and as HumanFactory, you should export it using "named exports" instead of default exports. Here is one way to do it:

export interface Human {
    name: string;
    age: number;
}

export class HumanFactory {
    getHuman(): Human {
        return {
            name: "John",
            age: 22,
        }
    }
}

In your index.ts file:

import { Human, HumanFactory } from "./lib";

** EDIT **

If modifying lib.d.ts is not an option, you can redefine Human and use double-casting like this:

import HumanFactory from "./lib";

interface Human {
    name: string;
    age: number;
}

export class Foo {
    human: Human;

    constructor() {
        const factory = new HumanFactory();
        this.human = factory.getHuman() as any as Human; 
    }

    diffWithError(age: number): number {
        return age - this.human.name;
    }

    diffWithTypingAndAutocoplete(age: number): number {
        const factory = new HumanFactory();
        return age - factory.getHuman().name;
    }
}

Answer №4

With the release of TypeScript 2.8, a new static type called ReturnType<> was introduced, making this task simpler.

import PersonFactory from "./lib";
type Person = ReturnType<typeof PersonFactory.prototype.getPerson>

To learn more, visit

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to access and troubleshoot the complete source code within .vue files?

I've been struggling for hours trying to understand why I'm unable to view the full source of my .vue files in the Chrome debugger. When I click on webpack://, I can see the files listed there like they are in my project tree, but when I try to o ...

What is the importance of having a reference path for compiling an AngularJS 2 project using gulp-typescript?

I wanted to modify the Angular Tour Of Heros project to utilize gulp from this Github Repository. This is the gulpfile.json file I came up with: const gulp = require('gulp'); const del = require('del'); const typescript = require(&apo ...

Unlock the power of Angular ViewChildren to access and manipulate SVG elements efficiently

I have an SVG file loaded as an object: <object data="assets/img/states.svg" type="image/svg+xml" id="map"></object> This SVG includes a large PNG map along with several rect and text elements. <rect y="224.72084" x="644.87109" ...

Sending data with an Http POST request in Angular 2

I'm having difficulty with a POST request that I am trying to make: sendRequest() { var body = 'username=myusername&password=mypassword'; var headers = new Headers(); headers.append('Content-Type', 'applicat ...

What is the best way to access the vue3datepicker object in order to manually close the date picker popup user interface?

Enhancement After yoduh's feedback, I made adjustments to the code below. However, vue3datepicker is still undefined. Code has been updated according to yodubs suggestion. I consulted the official vue3datepicker documentation to customize my own Act ...

Is it possible for TypeScript to automatically determine the type of an imported module based on its path?

I'm currently working on creating a function, test.isolated(), which wraps around jest.isolateModules. This function takes an array of strings representing the modules to be imported, along with the usual arguments (name, fn, timeout), and then inject ...

I am currently struggling with a Typescript issue that I have consulted with several individuals about. While many have found a solution by upgrading their version, unfortunately, it

Error message located in D:/.../../node_modules/@reduxjs/toolkit/dist/configureStore.d.ts TypeScript error in D:/.../.../node_modules/@reduxjs/toolkit/dist/configureStore.d.ts(1,13): Expecting '=', TS1005 1 | import type { Reducer, ReducersMapO ...

Ensuring TypeORM constraint validations work seamlessly with MySQL and MariaDB

I recently started using TypeORM and I'm trying to incorporate the check decorator in my MySQL/MariaDB database. However, after doing some research on the documentation and online, it seems that the check decorator is not supported for MySQL. I'v ...

What is the best way to convert this into a distinct function using typescript?

Is there a way to create a single method in Protractor or Webdriver API that can get the browser width and height? const getWindowWidth = async () => { const size = await browser.manage().window().getSize(); return size.width; }; I need this metho ...

Receive regular updates every week for an entire month using Javascript

How can I calculate the number of check-ins per week in a month using Javascript? I have been unable to find relevant code for this task. Specifically, I am interested in determining the total count of user check-ins on a weekly basis. For example, if a u ...

Angular progress tracker with stages

I have been exploring ways to create a progress bar with steps in Angular 12 that advances based on the percentage of progress rather than just moving directly from one step to another. This is specifically for displaying membership levels and indicating h ...

Using the Ngclass function with a pair of objects

Can you include 2 objects in an ngclass function like this? <div class="progress-bar"[ngClass]="getProgressValues(obj.val1,obj.val2)"> </div> I am encountering a JSON error. SyntaxError: JSON.parse: bad control character in string literal at l ...

Can child components forward specific events to their parent component?

I created a basic component that triggers events whenever a button is clicked. InnerComponent.vue <template> <v-btn @click="emit('something-happened')">Click me</v-btn> </template> <script setup lang=" ...

Refresh Angular component upon navigation

I have set up routes for my module: const routes: Routes = [ { path: ":level1/:level2/:level3", component: CategoriesComponent }, { path: ":level1/:level2", component: CategoriesComponent}, { path: ":level1", component: ...

The SrollToTop function is ineffective when used with a component in Ionic 6/Angular

Recently, I implemented a fabbutton feature that allows users to scroll to the top of a page with just one click. Initially, I tested this functionality without using it as a component, and everything worked perfectly. However, now I want to turn this fabb ...

Mongoose: An unexpected error has occurred

Recently, I developed an express app with a nested app called users using Typescript. The structure of my app.js file is as follows: ///<reference path='d.ts/DefinitelyTyped/node/node.d.ts' /> ///<reference path='d.ts/DefinitelyTyp ...

Looking for a regular expression to verify if the URL inputted is valid in TypeScript

After conducting thorough research, I discovered that none of the suggested URLs met my criteria, prompting me to raise a new query. Here are my specific requirements: * The URL may or may not include 'http' or 'https' * The URL can co ...

The Formik and React error is indicating that the '{ refetch: any; }' type is absent

When attempting to pass a prop down to my EmailSignupScreen, I encountered an error message. This issue arose while experimenting with Formik and Typescript. "message": "Type '{ refetch: any; }' is missing the following properties from type &apo ...

The most efficient and hygienic method for retrieving a value based on an observable

Looking at the structure of my code, I see that there are numerous Observables and ReplaySubjects. When trying to extract a value from one of these observables in the HTML template, what would be the most effective approach? In certain situations, parame ...

I noticed that when using Next.js with the `revalidate: 1` option on a static page, it is triggering two full F5 refresh actions instead of just one. I was hoping for

Currently, I have set up a blog post edit page in my Next.js project. The post pages are utilizing the Incremental Static Regeneration feature with a revalidation time of 1 second for testing purposes. In the future, I plan to increase this to a revalidat ...