Home > Net >  Property not being recognized when I think it should be
Property not being recognized when I think it should be

Time:09-22

I have the following vocabulary:

type MyHeaders = {
  Authorization: string;
  Accept: "application/json";
};
type MyGetOptions = {
  url: string;
  json: true;
};
type MyOptionsWithHeaders = {
  headers: MyHeaders;
};
type MyPostOptions<T> = MyGetOptions | {
  body: T;
};
type MyPostOptionsWithHeaders<T> = MyPostOptions<T> | MyOptionsWithHeaders;
type MyBodyType = {};
type APICallOptions = MyPostOptionsWithHeaders<MyBodyType>;

The following code has an error under the "url" in "temp.url": "Property 'url' does not exist on type 'BatchSubmissionOptions'. Property 'url' does not exist on type 'OptionsWithHeaders'.ts(2339)"

const temp: APICallOptions = {
  url: "url",
  headers: {
    Authorization: "Bearer token",
    Accept: "application/json",
    },
  body: {
    some: "stuff",
    more_stuff: []
  }
}
temp.url = "Hello"

I am trying to define a vocabulary that allows me to specify "BatchSubmissionOptions" as the arguments to a particular query to a particular API in an internal server. I want to be able to define something as a Post, or a Post with authentication headers, or a Get, or a Get with authentication headers. It allows me to set the "url" properly not the "temp" object, but it objects when I set the property after initialization.

Have I mis-defined something without knowing it?

EDIT: On the advice of CodeQuiver, I have updated the code with more standards-compliant separators according to the article he cited. While it is likely good advice, it did not resolve the problem.

Footnote: As I simplify this problem for this question, I'm also noticing that it isn't objecting to the fact that the value of "body" doesn't conform to MyBodyType, and it's allowing that; it correctly enforces things when I define properties in MyBodyType; maybe that's a feature of defining an object as {}?

CodePudding user response:

If you have a variable typed with a union type, e.g.

let temp: A | B;

you can only access members which are defined in both types using that variable. The compiler wants to protect you from accessing non-existing members (which could cause your program to crash), since it cannot know to which type the variable resolves.

Example

We define two types A and B with one overlapping property id:

type A = {
    id: number;
    title: string;
    text: string;
}

type B = {
    id: number;
    flag1: boolean;
    flag2: boolean;
}

The following will work:

const id = temp.id;

...while accessing one of the other properties won't:

const text = temp.text;   // <--- Error: "text" does not exist in B

On the other hand, if you assign an object literal, you must define at least one non-overlapping member, so that the compiler can decide for one type and warn you about missing members.

The following will throw an error (Type '{ id: number }' is missing the following properties from type 'B': flag1, flag2):

let temp: A | B = {
    id: 1
};

If you now define one or additional non-overlapping members, the compiler can decide for one type and checks whether all required members have been defined. The following definition points the compiler to type A:

let temp: A | B = {
    id: 1,
    title: "Title"
};

Now it will complain about missing text. If you add it the error will go away. It is also valid to add members from type B, since temp is defined as union type:

let temp: A | B = {
    id: 1,
    title: "Title",
    text: "Text",
    flag1: true
};

CodePudding user response:

I believe the main problem is actually a simple typo. You have not separated the members of your type literal with anything here:

type MyGetOptions = {
  url: string
  json: true
}

When Typescript requires separation either using semicolons or commas, as in your first type literal:

type MyHeaders = {
  Authorization: string,
  Accept: "application/json",
}

Therefore it cannot find the 'url' property due to it not being defined correctly in the very beginning.

Of course it is possible there is something more complex going on, but I would suggest fixing the simplest problem first in case that does solve it.

See this Medium article for further details: https://medium.com/swlh/typescript-best-practices-delimiters-and-ordering-7feb42e0ca1d (I couldn't find explicit mention of this syntax in the Typescript documentation, which is why I am citing Medium instead, but all the examples used semicolon separators which supports my assertion.)

  • Related