1

I am trying to experiment with typescript namespaces. It works perfectly when i dont import other modules but when I import other modules in my case sort the exported function get cant be used in other file even though they are in same namespace.

import { sort } from "../sort"

namespace Api {
    export async function get(url:string){
        const res = await fetch("api/"+url)
        return await res.json()
    }
    export class Users {
        private uid: string
        constructor(uid: string){
            this.uid = uid
        }
        public async getUserProfile(){
            return await get(`/u/${this.uid}/profile`)
        }
        public async getUserSongs(){
            return await get(`/u/${this.uid}/musics`)
        }
    }
}

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

namespace Api {
    export class Track{
        private track_id: string
        constructor(track_id){
            this.track_id = track_id
        }
        public async hasLiked(){
            return await get("/track/"+this.track_id+"/hasLiked")
        }
    }
}

It says could not find name get

But when i dont import sort it works perfectly

namespace Api {
    export async function get(url:string){
        const res = await fetch("api/"+url)
        return await res.json()
    }
    export class Users {
        private uid: string
        constructor(uid: string){
            this.uid = uid
        }
        public async getUserProfile(){
            return await get(`/u/${this.uid}/profile`)
        }
        public async getUserSongs(){
            return await get(`/u/${this.uid}/musics`)
        }
    }
}

1 Answer 1

1

When you add import statement to the file it turns that .ts file into a module, and "moves" all declarations in the file into the scope of that module.

So namespace Api is not seen outside of its module definition and removes it from global namespace. There are a few ways to deal with it:

  1. You can turn you main Api file into definition file by changing its extension to d.ts and wrap namespace Api into declare global {}. By that you don't even need triple slash import, it will still be available in global scope. Your new api.d.ts should look like this:

import { sort } from "./sort";

declare global {
  namespace Api {
    export async function get(url: string) {
      const res = await fetch("api/" + url);
      return await res.json();
    }
    export class Users {
      private uid: string;
      constructor(uid: string) {
        this.uid = uid;
      }
      public async getUserProfile() {
        return await get(`/u/${this.uid}/profile`);
      }
      public async getUserSongs() {
        return await get(`/u/${this.uid}/musics`);
      }
    }
  }
}

  1. Ommiting top-level import and using dynamic import like this:

namespace Api {
  export async function get(url: string) {
    const sort = await import("./sort");
    const res = await fetch("api/" + url);
    return await res.json();
  }
  export class Users {
    private uid: string;
    constructor(uid: string) {
      this.uid = uid;
    }
    public async getUserProfile() {
      return await get(`/u/${this.uid}/profile`);
    }
    public async getUserSongs() {
      return await get(`/u/${this.uid}/musics`);
    }
  }
}

  1. Simply turning namespace Api with Track into d.ts resolves the issue

Working with declaration merging and modules in typescript is pretty complex and i think there are still many workarounds and pitfalls about it so my answer can definitely be extended.

Sign up to request clarification or add additional context in comments.

1 Comment

Ive been looking for the solution for this problem for a while and i couldn’t get anything. This answer will help me alot thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.