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

Attempting to adhere to the prescribed Cypress tutorial is resulting in various errors related to being "compiled under '--isolatedModules'"

I am new to using Cypress and I have been following the helpful tutorial on testing your first application. However, I have encountered some compiler issues in the third section. Following the instructions, I created a custom command but I am receiving th ...

Is it possible for me to create an If statement that can verify the current index within a Map?

I have the following TypeScript code snippet: export default class SingleNews extends React.Component<INews, {}> { public render(): React.ReactElement<INews> { return ( <> {this.props.featured ...

What could be the reason behind my Vue file triggering a TS6504 error and suggesting to "allowJs"?

My Vue 3 project, which incorporates TypeScript, is encountering an issue when I attempt to execute vue-tsc --noEmit. error TS6504: File '/proj/src/pages/Index.vue.__VLS_template.jsx' is a JavaScript file. Did you mean to enable the 'allowJs ...

Guide on categorizing MUI icon types

My current code snippet is as follows: type MenuItem = { key: string; order: number; text: string; icon: typeof SvgIcon; }; However, when I attempt to use it in my JSX like this: <List> {MENU.map((menuItem: MenuItem) => ( ...

I am having trouble getting the guide for setting up a NextJS app with Typescript to function properly

For some time now, I have been experimenting with integrating Typescript into my NextJS projects. Initially, I believed that getting started with Typescript would be the most challenging part, but it turns out that installing it is proving to be even more ...

Error encountered while transforming object due to index type mismatch

I am attempting to change the values of an object, which consist of arrays with numbers as keys, to their respective array lengths. However, I received a type error that says 'Element implicity has any type because a string element cannot be used to ...

Ways to redirect to a different page following a successful execution of a mutation in React-query

I am facing an issue where a memory leak warning appears when I redirect to another page after a mutation. Despite trying various methods, I have not been able to find a solution. The specific warning message is: Warning: Can't perform a React state ...

How does the call method on array.prototype.includes work with arguments x and y?

Curious about the functionality of array.prototype.includes.call(x, y);. Discovered that includes() confirms if an array has the specified value and provides a true or false result. Learned that call() invokes this alongside any optional arguments. The ...

The key is not applicable for indexing the type as expected

Here is the TS code I am working with: type Fruit = { kind: "apple" } | { kind: "grape"; color: "green" | "black" }; type FruitTaste<TFruit extends Fruit> = TFruit["kind"] extends "apple" ? "good" : TFruit["color"] extends "green" ? "good" : ...

Is there a method to make this package compatible with Angular version 16?

I recently integrated the ngx-hotjar package version 11.0.0 into my Angular 10 project with success. However, when trying to use it in a new Angular 16 project, I encountered the following error during ng serve: Error: src/app/app.module.ts:275:12 - error ...

"Utilizing variadic tuple types to implement the pipe function in TypeScript 4: A step-by-step guide

An illustration from the release notes of TypeScript 4 demonstrates the use of variadic tuple types to eliminate multiple overload definitions. It seems feasible to type the pipe function for any number of arguments. type F<P, R> = (p: P) => R ty ...

Does it follow standard practice for Array.filter to have the capability to also perform mapping on an array of objects?

While experimenting with Array.filter, I made an interesting discovery. By forgetting to include an equality check, my array was unexpectedly mapped instead of filtered. Here is the code snippet that led to this result: const x = [{ name: 'user' ...

Tips for dividing by a large number

I am currently attempting the following: const numerator = 268435456; const denominator = 2 ** 64; const decimalFraction = numerator / denominator; In order to achieve this, I have experimented with utilizing the code provided in this link: : const rawVal ...

Angular 2 rc1 does not support ComponentInstruction and CanActivate

In the process of developing my Angular 2 application with Typescript using angular 2 rc.1, I've noticed that the official Angular 2 documentation has not been updated yet. I had references to ComponentInstruction Interface and CanActivate decorator ...

What could be the reason behind the for loop not running within a typescript function?

My confusion lies in the for loop within this function that seems to never run. Each console log is set up to return a specific value, but the looping action doesn't trigger. Can someone provide insight into what might be causing this issue? export fu ...

Strange behavior when working with Typescript decorators and Object.defineProperty

I'm currently working on a project that involves creating a decorator to override a property and define a hidden property. Let's take a look at the following example: function customDecorator() { return (target: any, key: string) => { ...

The specified 'Object' type does not match the required 'Document' constraint

I need assistance with running a MERN application to check for any issues, but I keep encountering this error across multiple files. Error: The 'CatalogType' type does not meet the requirements of 'Document'. The 'CatalogType&apo ...

What is the best way to compile TypeScript files without them being dependent on each other?

I have created a TypeScript class file with the following code: class SampleClass { public load(): void { console.log('loaded'); } } Now, I also have another TypeScript file which contains functions that need to utilize this class: // ...

The Angular JavaScript page successfully compiles, yet displays only a blank screen

I am facing an issue with my Angular app where it compiles successfully, but the HTML page appears blank and my application is not displaying properly. I have encountered similar problems in the past which were often related to Imports, but this time I&apo ...

Ways to sequentially execute API calls rather than concurrently

Update: Find the complete solution at the end of this answer. Consider the following code snippet: @Injectable() export class FileUploader { constructor(private http: Http) {} upload(url: string, file: File) { let fileReader: FileReader ...