Skip to content
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

Merged
merged 10 commits into from
Jan 6, 2017
2 changes: 2 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3145,6 +3145,7 @@ namespace ts {
case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
Expand Down Expand Up @@ -3343,6 +3344,7 @@ namespace ts {
case SyntaxKind.NumberKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
Expand Down
11 changes: 11 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ namespace ts {
const voidType = createIntrinsicType(TypeFlags.Void, "void");
const neverType = createIntrinsicType(TypeFlags.Never, "never");
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");

const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);

Expand Down Expand Up @@ -4178,6 +4179,7 @@ namespace ts {
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NullKeyword:
Expand Down Expand Up @@ -4759,6 +4761,7 @@ namespace ts {
t.flags & TypeFlags.NumberLike ? globalNumberType :
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType() :
t.flags & TypeFlags.NonPrimitive ? globalObjectType :
t;
}

Expand Down Expand Up @@ -6384,6 +6387,8 @@ namespace ts {
return nullType;
case SyntaxKind.NeverKeyword:
return neverType;
case SyntaxKind.ObjectKeyword:
return nonPrimitiveType;
case SyntaxKind.JSDocNullKeyword:
return nullType;
case SyntaxKind.JSDocUndefinedKeyword:
Expand Down Expand Up @@ -7139,6 +7144,8 @@ namespace ts {
if (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum && isEnumTypeRelatedTo(<EnumType>source, <EnumType>target, errorReporter)) return true;
if (source.flags & TypeFlags.Undefined && (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void))) return true;
if (source.flags & TypeFlags.Null && (!strictNullChecks || target.flags & TypeFlags.Null)) return true;
if (source.flags & TypeFlags.Object && target === nonPrimitiveType) return true;
if (source.flags & TypeFlags.Primitive && target === nonPrimitiveType) return false;
if (relation === assignableRelation || relation === comparableRelation) {
if (source.flags & TypeFlags.Any) return true;
if ((source.flags & TypeFlags.Number | source.flags & TypeFlags.NumberLiteral) && target.flags & TypeFlags.EnumLike) return true;
Expand Down Expand Up @@ -9210,6 +9217,9 @@ namespace ts {
}

function getTypeFacts(type: Type): TypeFacts {
if (type === nonPrimitiveType) {
return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
}
const flags = type.flags;
if (flags & TypeFlags.String) {
return strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts;
Expand Down Expand Up @@ -18072,6 +18082,7 @@ namespace ts {
case "string":
case "symbol":
case "void":
case "object":
error(name, message, (<Identifier>name).text);
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2507,6 +2507,7 @@ namespace ts {
case SyntaxKind.SymbolKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.ObjectKeyword:
// If these are followed by a dot, then parse these out as a dotted type reference instead.
const node = tryParse(parseKeywordAndNoDot);
return node || parseTypeReference();
Expand Down Expand Up @@ -2565,6 +2566,7 @@ namespace ts {
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.ObjectKeyword:
return true;
case SyntaxKind.MinusToken:
return lookAhead(nextTokenIsNumericLiteral);
Expand Down Expand Up @@ -6026,6 +6028,7 @@ namespace ts {
case SyntaxKind.NullKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.ObjectKeyword:
return parseTokenNode<JSDocType>();
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ namespace ts {
"new": SyntaxKind.NewKeyword,
"null": SyntaxKind.NullKeyword,
"number": SyntaxKind.NumberKeyword,
"object": SyntaxKind.ObjectKeyword,
"package": SyntaxKind.PackageKeyword,
"private": SyntaxKind.PrivateKeyword,
"protected": SyntaxKind.ProtectedKeyword,
Expand Down
10 changes: 7 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ namespace ts {
ReadonlyKeyword,
RequireKeyword,
NumberKeyword,
ObjectKeyword,
SetKeyword,
StringKeyword,
SymbolKeyword,
Expand Down Expand Up @@ -816,6 +817,7 @@ namespace ts {
export interface KeywordTypeNode extends TypeNode {
kind: SyntaxKind.AnyKeyword
| SyntaxKind.NumberKeyword
| SyntaxKind.ObjectKeyword
| SyntaxKind.BooleanKeyword
| SyntaxKind.StringKeyword
| SyntaxKind.SymbolKeyword
Expand Down Expand Up @@ -2779,6 +2781,7 @@ namespace ts {
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
/* @internal */
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
NonPrimitive = 1 << 24, // intrinsic object type

/* @internal */
Nullable = Undefined | Null,
Expand All @@ -2788,7 +2791,7 @@ namespace ts {
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean,
/* @internal */
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never,
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
/* @internal */
Primitive = String | Number | Boolean | Enum | ESSymbol | Void | Undefined | Null | Literal,
StringLike = String | StringLiteral | Index,
Expand All @@ -2802,8 +2805,8 @@ namespace ts {

// 'Narrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol,
NotUnionOrUnit = Any | ESSymbol | Object,
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol | NonPrimitive,
NotUnionOrUnit = Any | ESSymbol | Object | NonPrimitive,
/* @internal */
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
/* @internal */
Expand Down Expand Up @@ -2857,6 +2860,7 @@ namespace ts {
ObjectLiteral = 1 << 7, // Originates in an object literal
EvolvingArray = 1 << 8, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
NonPrimitive = 1 << 10, // NonPrimitive object type
ClassOrInterface = Class | Interface
}

Expand Down
14 changes: 14 additions & 0 deletions tests/baselines/reference/assignObjectToNonPrimitive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//// [assignObjectToNonPrimitive.ts]
var x = {};
var y = {foo: "bar"};
var a: object;
a = x;
a = y;


//// [assignObjectToNonPrimitive.js]
var x = {};
var y = { foo: "bar" };
var a;
a = x;
a = y;
19 changes: 19 additions & 0 deletions tests/baselines/reference/assignObjectToNonPrimitive.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
=== tests/cases/conformance/types/nonPrimitive/assignObjectToNonPrimitive.ts ===
var x = {};
>x : Symbol(x, Decl(assignObjectToNonPrimitive.ts, 0, 3))

var y = {foo: "bar"};
>y : Symbol(y, Decl(assignObjectToNonPrimitive.ts, 1, 3))
>foo : Symbol(foo, Decl(assignObjectToNonPrimitive.ts, 1, 9))

var a: object;
>a : Symbol(a, Decl(assignObjectToNonPrimitive.ts, 2, 3))

a = x;
>a : Symbol(a, Decl(assignObjectToNonPrimitive.ts, 2, 3))
>x : Symbol(x, Decl(assignObjectToNonPrimitive.ts, 0, 3))

a = y;
>a : Symbol(a, Decl(assignObjectToNonPrimitive.ts, 2, 3))
>y : Symbol(y, Decl(assignObjectToNonPrimitive.ts, 1, 3))

24 changes: 24 additions & 0 deletions tests/baselines/reference/assignObjectToNonPrimitive.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
=== tests/cases/conformance/types/nonPrimitive/assignObjectToNonPrimitive.ts ===
var x = {};
>x : {}
>{} : {}

var y = {foo: "bar"};
>y : { foo: string; }
>{foo: "bar"} : { foo: string; }
>foo : string
>"bar" : "bar"

var a: object;
>a : object

a = x;
>a = x : {}
>a : object
>x : {}

a = y;
>a = y : { foo: string; }
>a : object
>y : { foo: string; }

10 changes: 10 additions & 0 deletions tests/baselines/reference/nonPrimitiveAccessProperty.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAccessProperty.ts(3,3): error TS2339: Property 'nonExist' does not exist on type 'object'.


==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAccessProperty.ts (1 errors) ====
var a: object;
a.toString();
a.nonExist(); // error
~~~~~~~~
!!! error TS2339: Property 'nonExist' does not exist on type 'object'.

10 changes: 10 additions & 0 deletions tests/baselines/reference/nonPrimitiveAccessProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//// [nonPrimitiveAccessProperty.ts]
var a: object;
a.toString();
a.nonExist(); // error


//// [nonPrimitiveAccessProperty.js]
var a;
a.toString();
a.nonExist(); // error
18 changes: 18 additions & 0 deletions tests/baselines/reference/nonPrimitiveAsProperty.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAsProperty.ts(7,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'WithNonPrimitive'.
Types of property 'foo' are incompatible.
Type 'string' is not assignable to type 'object'.


==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAsProperty.ts (1 errors) ====
interface WithNonPrimitive {
foo: object
}

var a: WithNonPrimitive = { foo: {bar: "bar"} };

var b: WithNonPrimitive = {foo: "bar"}; // expect error
~
!!! error TS2322: Type '{ foo: string; }' is not assignable to type 'WithNonPrimitive'.
!!! error TS2322: Types of property 'foo' are incompatible.
!!! error TS2322: Type 'string' is not assignable to type 'object'.

13 changes: 13 additions & 0 deletions tests/baselines/reference/nonPrimitiveAsProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//// [nonPrimitiveAsProperty.ts]
interface WithNonPrimitive {
foo: object
}

var a: WithNonPrimitive = { foo: {bar: "bar"} };

var b: WithNonPrimitive = {foo: "bar"}; // expect error


//// [nonPrimitiveAsProperty.js]
var a = { foo: { bar: "bar" } };
var b = { foo: "bar" }; // expect error
54 changes: 54 additions & 0 deletions tests/baselines/reference/nonPrimitiveAssignError.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(5,1): error TS2322: Type 'object' is not assignable to type '{ foo: string; }'.
Property 'foo' is missing in type 'Object'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(13,1): error TS2322: Type 'number' is not assignable to type 'object'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(14,1): error TS2322: Type 'true' is not assignable to type 'object'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(15,1): error TS2322: Type 'string' is not assignable to type 'object'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(17,1): error TS2322: Type 'object' is not assignable to type 'number'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(18,1): error TS2322: Type 'object' is not assignable to type 'boolean'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(19,1): error TS2322: Type 'object' is not assignable to type 'string'.


==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts (7 errors) ====
var x = {};
var y = {foo: "bar"};
var a: object;
x = a;
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

y = a; // expect error
~
!!! error TS2322: Type 'object' is not assignable to type '{ foo: string; }'.
!!! error TS2322: Property 'foo' is missing in type 'Object'.
a = x;
a = y;

var n = 123;
var b = true;
var s = "fooo";

a = n; // expect error
~
!!! error TS2322: Type 'number' is not assignable to type 'object'.
a = b; // expect error
~
!!! error TS2322: Type 'true' is not assignable to type 'object'.
a = s; // expect error
~
!!! error TS2322: Type 'string' is not assignable to type 'object'.

n = a; // expect error
~
!!! error TS2322: Type 'object' is not assignable to type 'number'.
b = a; // expect error
~
!!! error TS2322: Type 'object' is not assignable to type 'boolean'.
s = a; // expect error
~
!!! error TS2322: Type 'object' is not assignable to type 'string'.

var numObj: Number = 123;
var boolObj: Boolean = true;
var strObj: String = "string";

a = numObj; // ok
a = boolObj; // ok
a = strObj; // ok

53 changes: 53 additions & 0 deletions tests/baselines/reference/nonPrimitiveAssignError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//// [nonPrimitiveAssignError.ts]
var x = {};
var y = {foo: "bar"};
var a: object;
x = a;
y = a; // expect error
a = x;
a = y;

var n = 123;
var b = true;
var s = "fooo";

a = n; // expect error
a = b; // expect error
a = s; // expect error

n = a; // expect error
b = a; // expect error
s = a; // expect error

var numObj: Number = 123;
var boolObj: Boolean = true;
var strObj: String = "string";

a = numObj; // ok
a = boolObj; // ok
a = strObj; // ok


//// [nonPrimitiveAssignError.js]
var x = {};
var y = { foo: "bar" };
var a;
x = a;
y = a; // expect error
a = x;
a = y;
var n = 123;
var b = true;
var s = "fooo";
a = n; // expect error
a = b; // expect error
a = s; // expect error
n = a; // expect error
b = a; // expect error
s = a; // expect error
var numObj = 123;
var boolObj = true;
var strObj = "string";
a = numObj; // ok
a = boolObj; // ok
a = strObj; // ok
Loading