-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Fix #1809, introduce non primitive object type #12501
Conversation
@@ -1662,6 +1663,13 @@ namespace ts { | |||
return type; | |||
} | |||
|
|||
function createNonPrimitiveType(): ResolvedType { | |||
const type = setStructuredTypeMembers( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ahejlsberg can judge better, but I don't think you want to set any structured type members - I think you want to just need to add a branch in getApparentType
for object
.
@@ -3042,6 +3053,10 @@ namespace ts { | |||
return type && (type.flags & TypeFlags.Never) !== 0; | |||
} | |||
|
|||
function isTypeNonPrimitive(type: Type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only used in one place right now, so consider just inlining.
@HerringtonDarkholme thanks for the PR! As a heads up, we'll probably have to discuss this more at a design meeting, but I think we can iterate on it for a bit. I think that a change like this would need the following tests:
Most of these should all run under |
Thanks for your quick response and kind suggestion! I'm quite unsure about the behavior of narrowing behavior. For example, an empty class like Thanks for the review! I will research other issues! |
If I'm not mistaken TypeScript uses the declared type (i.e. the original type) when getting the left side of an assignment. In those cases, the type should still be |
This is a great feature, specially when dealing with generic constraints. Can it fully cover the bellow case? interface Proxy<T extends object> {
// ...
}
// fails:
const a: Proxy<number> = { ... };
const c: Proxy<null> = { ... };
const c: Proxy<undefined> = { ... };
// won't fail:
interface Blah {
foo: number;
}
const b: Proxy<Blah> = { ... }; |
Thanks for all your valuable suggestion! |
Question: what about functions? They're also reference types that can be Edit: I don't know what I'm talking about... they implicitly subtype |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the semantics but I agree with @DanielRosenwasser that (1) we need to have the whole team discuss at a design meeting (2) nonPrimitiveType
should be a simple marker type, more like stringType
, numberType
etc and then add a branch in getApparentType
. IntrinsicType
with TypeFlags.Object
might even work.
@@ -0,0 +1,31 @@ | |||
tests/cases/conformance/types/nonPrimitive/nonPriimitiveInFunction.ts(12,12): error TS2345: Argument of type 'boolean' is not assignable to parameter of type 'object'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo in filename: Priimitive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments on the tests
var x = {}; | ||
var y = {foo: "bar"}; | ||
var a: object; | ||
x = a; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about a = x
and a = y
? I think neither should have errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
~~~~~~ | ||
!!! error TS2344: Type 'number' does not satisfy the constraint 'object'. | ||
var y: Proxy<null>; // ok | ||
var z: Proxy<undefined> ; // ok |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
line 51 and 52 are only ok because strictNullChecks is not turned on, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and there are corresponding tests in nonPrimitiveStrictNull.ts
which produces errors.
|
||
|
||
==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveInGeneric.ts (7 errors) ==== | ||
function generic<T>(t: T) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except for null and undefined, I don't think these tests are that valuable, since they are just testing assignability again
@sandersn Pardon my question. I wonder if nonPrimitiveType can be labelled as Maybe I can change the TypeFlags? Or patching more functions? |
Yeah, I talked to @ahejlsberg and he said that |
Thanks for the clarification! I'm quite hesitant about adding TypeFlags since TypeScript will have more features like variadic type parameter and exact type, which all need TypeFlag entries. I think I will wait for your whole team discussion. |
I have updated the implementation to use intrinsic type. One thing I'm not sure is whether intrinsic object should be counted as But if intrinsic type is used in I'm very grateful to you for all your review! 😃 Update: |
I don't think you should re-use |
93b6354
to
8993877
Compare
Thanks for your advice! I have changed |
Thanks so much @HerringtonDarkholme! (And sorry for the delay from the holidays.) |
One common task is to take an existing type and make each of its properties entirely optional. The issue with using Partial is that any usages of Partial also accepts primitives, even though the intent is that primitives be excluded. Is this new object type, added with 2.2, used by default when using Partial construct ? It would make sense to make use of it, since it would solve the issue of usages of Partial also accepting primitives. |
@glazar That seems reasonable. Can you create an issue for that so that we can track it separately? The PR should be simple. |
If that were the case, wouldn't it break the currently used "deep partial" implementations? The same applies to |
@gcnew can you give an example of a deep partial type? The As I recall, the basic problem with a recursive partial type is that we have no way to stop the recursion right now, so it does in fact recur right though primitives, chopping them up into |
@sandersn I don't have anything specific in mind. I think I've seen people asking question and submitting hacky code that might be affected. On a more serious note, will it work correctly if the target type has type parameters? |
Fixes #1809. I found object primitive type is quite confusing. Because what it means is that other primitive types cannot be assigned to. So I change the name to non primitive.
non primitive type is quite useful for ECMA API like Object.create, Object.setPrototype and Proxy.
It is also useful for userland libraries such as immutable and backbone.