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:
helperInt
and helperObj
accept both a Req
typed object or a literal objectget
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
functionThis 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.