Skip to content

Unsoundness in coercion from object to boolean #31464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
uhyo opened this issue May 19, 2019 · 9 comments
Closed

Unsoundness in coercion from object to boolean #31464

uhyo opened this issue May 19, 2019 · 9 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@uhyo
Copy link
Contributor

uhyo commented May 19, 2019

TypeScript Version: 3.4.3, [email protected]

Search Terms: unsound coercion object boolean

Code

const obj: { toFixed: unknown } = 0;
// val has type "123"
const val = obj && "123";
console.log(val.slice()); // <- runtime error!

Expected behavior:
Compile error.

Actual behavior:
This code compiles and causes a runtime error when run.

TypeScript assumes that any value with object type (except {}) is truthy, but it's not true as illustrated above. If one of 0, "", false or 0n is assignable to an object type, it should not be considered truthy.

Playground Link: https://www.typescriptlang.org/play/#src=const%20obj%3A%20%7B%20toFixed%3A%20unknown%20%7D%20%3D%200%3B%0A%2F%2F%20val%20has%20type%20%22123%22%0Aconst%20val%20%3D%20obj%20%26%26%20%22123%22%3B%0Aconsole.log(val.slice())%3B%0A%0A%0A

Related Issues: #18196 (fixed by #27157)

@lll000111
Copy link

Unsoundness in coercion from object to boolean

Isn't the unsoundness the very first line

const obj: { toFixed: unknown } = 0;

Coercing a primitive (number) to an object?

@shqld
Copy link

shqld commented May 19, 2019

At a single glance it seems TypeScript should distinguish number from Number instance?

E.g. when we annotate a certain variable as Number specifically, TS should regard it as an object with some props & methods it should have but otherwise as if it'd be a real primitive value same as other languages.

@MartinJohns
Copy link
Contributor

Coercing a primitive (number) to an object?

It is not coercing a primitive to an object. The type { toFixed: unknonw } does not constrain to objects. It just says whatever is passed has a toFixed property.

If you want to enforce having an object, then you can make use of the object type:

const obj1: { toFixed: unknown } & object = 0; // Does not compile
const obj2: { toFixed: unknown } & object = { toFixed: 0 }; // Does compile

@lll000111
Copy link

lll000111 commented May 20, 2019

@MartinJohns

Is this bad a joke?

It just says whatever is passed has a toFixed property.

Which means it is an object. Primitives don't have properties. Your comment is factually sooo wrong... do we really need to discuss the very basics of JS? (Rhetorical question)

> 1.toFixed()
VM43:1 Uncaught SyntaxError: Invalid or unexpected token

> Number(1).toFixed()
"1"

@uhyo Why do you upvote factually obviously wrong stufff? Just curious. Please learn the very basics of the language, it seems you have serious gaps if you think primitives in JS have properties.

https://javascriptweblog.wordpress.com/2010/09/27/the-secret-life-of-javascript-primitives/

@MartinJohns
Copy link
Contributor

I was not precise enough. It doesn't has to be a property, but anything with the name 'toFixed', and numbers have a function named like that.

const n = 0;
console.log(n.toFixed);

@lll000111
Copy link

lll000111 commented May 20, 2019

@MartinJohns

but anything with the name 'toFixed',

OPs example has a primitive number!! Not an object! Primitive number 0 is not an object without coercion, in this language!

I really don't see the point of discussing language basics. You can learn all about primitives and objects in JS in free introductory Javascript courses.

@MartinJohns
Copy link
Contributor

MartinJohns commented May 20, 2019

My example has a primitive number as well. I just omitted the type information. My example does the same as his: store 0 in a variable.

@lll000111
Copy link

lll000111 commented May 20, 2019

Which is coerced to an object.

You do understand JS automatically does type coercion?

> typeof 0.toFixed
VM134:1 Uncaught SyntaxError: Invalid or unexpected token

> typeof Number(0).toFixed
"function"

A primitive number is not an object. Yes, you can coerce it to be one, news at 11. I thought you use static typing to not use such language features.

https://javascriptweblog.wordpress.com/2010/09/27/the-secret-life-of-javascript-primitives/

See under

If primitives have no properties, why does "abc".length return a value?

Because JavaScript will readily coerce between primitives and objects.
In this case the string value is coerced to a string object in order to access the property length. The string object is only used for a fraction of second after which it is sacrificed to the Gods of garbage collection

Your example explained right there, only using a string primitive instead of a number primitive.

Enough already, I'm out.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label May 20, 2019
@RyanCavanaugh
Copy link
Member

@lll000111 take it down a notch; we don't need to be condescending to have a discussion.

Anyway, as to the OP: Types written as { p: T; } are not constrained to be object-like. There is plenty of code that operates well on things that have length and are indexable (including strings) or things that have a toString (including numbers). This does create a soundness gap for the falsy primitive values, but it's not with any palatable solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

5 participants