Skip to content

typeToString fails to reduce subtypes #16582

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
sandersn opened this issue Jun 16, 2017 · 7 comments
Closed

typeToString fails to reduce subtypes #16582

sandersn opened this issue Jun 16, 2017 · 7 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@sandersn
Copy link
Member

Actual:
type reduction

Expected:
result1 and result2 both diplay their type as { a: number }

Thanks @lhecker for pointing out this problem.

@ahejlsberg
Copy link
Member

The issue here is that subtype reduction has observable effects. It definitely wouldn't be good for typeToString to display a type that is observably different from the actual type.

@lhecker
Copy link
Member

lhecker commented Jun 16, 2017

@ahejlsberg Can you explain that to me in simpler terms? I'm not entirely sure what you mean with "observably different".
In my understanding I thought that if, in the above example, both {a:1} as well as {a:2}, can be assigned to T when calling bar<T>, it should also be possible to reduce T1 | T2 down to just {a: number}. Wouldn't it be inconsistent otherwise? (I.e. because the arguments can be unified to T, but the return type can't be reduced to T.)

@jcalz
Copy link
Contributor

jcalz commented Jun 17, 2017

I'm not sure what @ahejlsberg is saying either.

I assume that if ts lets me declare the same variable twice with two type annotations, then the two annotated types are not observably different. For example, the lack of error in

var s: string;
var s: string | string;  // no error

witnesses that string is considered equivalent to string | string. Is that correct? Are these types observably different or the same?

If you look at

var a1 = { a: 1 };
var a2 = { a: 2 };
var x: typeof a1;
var x: typeof a2; // no error
var x: { a: number; }; // no error

it sure looks like the types of both the value { a: 1 } and the value { a: 2 } are equivalent to { a: number } and therefore equivalent to each other. If those types are "observably different" it seems like ts should not allow the above redeclaration, right?

But

var x: typeof a1;
var x: typeof a2; // no error, as before
var x: typeof a1 | typeof a1 // no error
var x: typeof a1 | typeof a2; // error!! 

gives the error message Variable 'x' must be of type '{ a: number; }', but here has type '{ a: number; } | { a: number; }'. as this issue is reporting. But this is inconsistent with the above. Application of the type operator typeof a1 | shouldn't be able to break equality.

So what am I missing? Thanks!

@jcalz
Copy link
Contributor

jcalz commented Jun 18, 2017

It occurs to me that the real issue is probably that union reduction in the type checker uses reference equality to recognize when to apply the law of idempotence (A | A reduces to A), instead of value equality. So it doesn't realize that two instances of { a : number } are equal, and we get this behavior.

Is it too expensive to compare types by value?

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jun 18, 2017

@ahejlsberg's point is that if you take the following:

var x = Math.random() < 0.5 ? { a: 100 } : { a: 100, b: 200 };

The type of x is { a: number } | { a: number, b: number }. We probably shouldn't display that as the subtype-reduced version { a: number }.

So instead of subtype reduction, you may want identity reduction. You may be right that we use reference equality for this @jcalz.

@jcalz
Copy link
Contributor

jcalz commented Jun 27, 2017

@DanielRosenwasser I'm trying to understand why we don't want subtype reduction to take place. Where does the difference between { a: number } and { a: number } | { a: number, b: number } show up? Is it only for object literal property checks or are there other reasons as well?

var x: { a: number } | { a: number, b: number };
x = {a: 100, b: 200}; // no excess property error
x = {a: 100, b: 'two hundred'}; // no error either!
x = {a: 100, b: 'two hundred', c: 3}; // excess property error

Is there anything close to a "canonical" discussion around subtype reduction where I can read more? (something like #4537?) Thanks.

@mhegazy mhegazy removed this from the TypeScript 2.6 milestone Aug 31, 2017
@mhegazy mhegazy added Design Limitation Constraints of the existing architecture prevent this from being fixed and removed Bug A bug in TypeScript labels Apr 13, 2018
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

8 participants