Home > database >  Error when passing an Object to a function expecting a Dictionnary?
Error when passing an Object to a function expecting a Dictionnary?

Time:01-28

I have the code below, where I want to pass a request object to an Http Get function

Why does get accept the object from helperObj and not the interface Req from helperInt

Link to Typescript Playground

interface Req {
    key: string;
    page?: number;
}

function helperInt(req: Req) {
    get('', req); // ERROR
    // Argument of type 'Req' is not assignable to parameter of type '{ [key: string]: string | number | boolean | (string | number | boolean)[]; }'.
    // Index signature for type 'string' is missing in type 'Req'.(2345)
}

function helperObj(req: {
    key: string;
    page?: number;
}) {
    get('', req);
}

function get(url: string, params?: { [key: string]: string | number | boolean | (string | number | boolean)[] }) {
    console.log(url, params);
}

const myReq: Req = {
    key: "foo",
    page: 0,
};

helperInt(myReq);
helperInt({
    key: "foo",
    page: 0,
});

helperObj(myReq);
helperObj({
    key: "foo",
    page: 0,
});

Notes:

  • Both helperInt and helperObj accept both a Req typed object or a literal object
  • The get function's params type is supposed to match Angular's HttpParams
  • Req interface is used to initialise an object, alter it if needed, before passing it to the get function

CodePudding user response:

This isn't really an Angular question, it's a Typescript question.

It boils down to:

interface Req {
    key: string;
    page?: number;
}

// Let's make a type alias for the type `get` expects.
type MyMap = { [key: string]: string | number | boolean | (string | number | boolean)[]; }

const myReq: Req = {key: "foo",page: 0}; // this works
const x: MyMap = { key: "foo", page: 0 }; // this works too

const y: MyMap = myReq; // Type 'Req' is not assignable to type 'MyMap'. Index signature for type 'string' is missing in type 'Req'

So what's the problem?

Well, if Typescript allowed this, you could do:

const x: Req = { key: 'foo', page: 0 };
const y: MyMap = x;
y.key = 0;
y.page = true;
console.log(x.key.toUppercase());

x has type Req, so static typing says x.key.toUppercase() is safe. But we've managed to assign a non-string to x.key, since x and y are references to the same object.

Therefore, Typescript disallows this.

One way to work around it is to spread the object into a new object.

const y: MyMap = { ...myReq };

Typescript can see that the object you're creating is consistent with MyMap, but since it's a new object subsequent changes won't break the type contract.

  • Related