28

How to declare JavaScript like nested objects in TypeScript?

let endpoints = {
    auth: {
        login: "http://localhost:8079/auth/login"
    }
};

The following does not work:

private endpoints: Object = {
    auth: {
        login: "http://localhost:8079/auth/login"
    }
};

Throws:

error TS2339: Property 'auth' does not exist on type 'Object'.

4 Answers 4

61

You can use interfaces:

interface EndpointAuth {
    login: string;
}

interface Endpoint {
    auth: EndpointAuth;
}

let endpoints: Endpoint = {
    auth: {
        login: "http://localhost:8079/auth/login"
    }
};

(code in playground)

You can also use types instead of interfaces:

type EndpointAuth = {
    login: string;
}

type Endpoint = {
    auth: EndpointAuth;
}

(code in playground)

Or "inline":

let endpoints: { auth: { login: string } } = {
    auth: {
        login: "http://localhost:8079/auth/login"
    }
};

You can combine them of course.


Edit

As you wanted the answer to explain why it did not work with Object:

Defining a variable to be of type Object is (in most cases) not what you really want to do, usually what you mean is any, as this:

var endpoints2: any = {
    auth: {
        login: "http://localhost:8079/auth/login"
    }
};

Won't fail (just like it won't if you do not specify a type).
Defining a variable as Object is the same as defining it as {} which is an empty object, and that's usually not what you're after, and it will work for only things like:

let o1: Object = {};
let o2: Object = Object.create(null);

But using any doesn't help you too much because then you basically tell the compiler not to bother with type safety, it will let you do what ever with the variable without letting you know that there are errors:

let o: any = { x: 3, y: 6 };
console.log(o.z.toString());

Won't fail in compilation but will fail at run time:

Uncaught TypeError: Cannot read property 'toString' of undefined

This will fail in compilation:

let o: { x: number, y: number } = { x: 3, y: 6 };
console.log(o.z.toString());
Sign up to request clarification or add additional context in comments.

3 Comments

I marked this as the correct answer, because it is the most comprehensive. However, Amir provided also an explanation on the nature of error. You could add this to your answer, if you like.
Fair enough. Added a detailed explanation.
let o2: Object = Object.create(null) would be incorrect, because you are saying o2's type is an object with Object as its prototype, but o2 has a null prototype.
17

You can declare an Interface.

For your case

interface IEndpoints
{
 auth: {
  login: string;
 }
}
private endpoints: IEndpoints = {
  auth: {
    login: "http://localhost:8079/auth/login"
  }
};

1 Comment

This should be the better answer. Why define two interfaces/types when all you need is one!
9

I don't know which typescript version you were using in the past, but currently, this is supported

interface Endpoints {
  [path: string]: Endpoints | string
}

const endpoints: Endpoints = {
    auth: {
        login: "http://localhost:8079/auth/login"
    }
}

2 Comments

____perfect____
I always get tripped up using Record<> which doesn't work with recursive types like {[path: string]: type} does :(
4

If you want to be type safe need to create your custom class\interface:

interface IMyInterface
{
    auth: IAuth;
}

interface IAuth
{
    login: string;
}

private endpoints: IMyInterface= {
    auth: {
        login: "http://localhost:8079/auth/login"
    }
};

Your error is because the your declaring endpoints of type Object, and Object doesn't have an auth property.

Comments

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.