Reputation: 612
I'm quite new to Typescript and work through a education video. Yesterday I found a weird behavior and think this is a bug. Example:
const json = '{"x": 10, "y":10}';
const coordinates: { x: number; y: number } = JSON.parse(json);
console.log(typeof coordinates.y);
This normally outputs x and y as type number. But it's not because of the type declaration, it's because of the JSON value. If you declare one of them as String, VS Code treats it like a String but internally it stays a number:
const json = '{"x": 10, "y":10}';
const coordinates: { x: number; y: string } = JSON.parse(json);
console.log(typeof coordinates.y);
This is the code:
As you see, VS Code treats it as a string
But the type checking proves that this isn't correct.
In my opinion it makes sense that an Object/Array after parsing has an any type. but it should be either message you an error if the parsed JSON-value is different to your annotation or it should morph the given value exact to the annotation.
I'm quite new to this, so if my assumption is wrong, please let me know!
Upvotes: 0
Views: 1914
Reputation: 370619
Typescript does inference before and during compilation. After compilation to JS, if some of your code's type logic is unsound, some of the types you've declared may be invalid. This is what's happening here.
JSON.parse
returns something of the type any
, which can be literally anything. You can try to extract properties from it with the line:
const coordinates: { x: number; y: string } = JSON.parse(json);
But this does not mean that the parsed object will actually have an x
property as a number, or a y
property as a string - your code there is telling the compiler to assume that it does, and to treat y
as a string later in the code.
If you have something which is, by default, of type any
, when extracting properties from it, you should make sure that its properties are really is of the type you're denoting them to be. If you're not 100% sure the properties will always be as expected (for example, if the input string comes from a network response - what if the response is 503
?), make a type guard, such as:
const isCoordinates = (param: unknown): param is { x: number, y: number } => {
return typeof param === 'object' && param !== null && 'x' in param;
};
Then call isCoordinates
before trying to extract properties from the string to make sure it's of the format you expect first.
Otherwise, like what's happening here, you can tell the compiler something false, and bugs and strange things may happen as a result.
You may consider enabling the tslint rule no-unsafe-any to avoid making these sort of mistakes.
Upvotes: 2