Skip to content

Commit

Permalink
Merge pull request #12501 from HerringtonDarkholme/non-primitive
Browse files Browse the repository at this point in the history
Fix #1809, introduce non primitive object type
  • Loading branch information
sandersn committed Jan 6, 2017
2 parents 91af4ae + 8993877 commit e9e7fce
Show file tree
Hide file tree
Showing 36 changed files with 978 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/compiler/binder.ts
Expand Up @@ -3146,6 +3146,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 @@ -3344,6 +3345,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
Expand Up @@ -148,6 +148,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 @@ -4196,6 +4197,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 @@ -4780,6 +4782,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 @@ -6405,6 +6408,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 @@ -7162,6 +7167,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 @@ -9235,6 +9242,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 @@ -18159,6 +18169,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
Expand Up @@ -2510,6 +2510,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 @@ -2568,6 +2569,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 @@ -6037,6 +6039,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
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
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 @@ -2785,6 +2787,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 @@ -2794,7 +2797,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 @@ -2808,8 +2811,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 @@ -2863,6 +2866,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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
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
@@ -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

0 comments on commit e9e7fce

Please sign in to comment.