Skip to content

Commit e9e7fce

Browse files
authored
Merge pull request #12501 from HerringtonDarkholme/non-primitive
Fix #1809, introduce non primitive object type
2 parents 91af4ae + 8993877 commit e9e7fce

36 files changed

+978
-7
lines changed

src/compiler/binder.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3146,6 +3146,7 @@ namespace ts {
31463146
case SyntaxKind.AnyKeyword:
31473147
case SyntaxKind.NumberKeyword:
31483148
case SyntaxKind.NeverKeyword:
3149+
case SyntaxKind.ObjectKeyword:
31493150
case SyntaxKind.StringKeyword:
31503151
case SyntaxKind.BooleanKeyword:
31513152
case SyntaxKind.SymbolKeyword:
@@ -3344,6 +3345,7 @@ namespace ts {
33443345
case SyntaxKind.NumberKeyword:
33453346
case SyntaxKind.NeverKeyword:
33463347
case SyntaxKind.StringKeyword:
3348+
case SyntaxKind.ObjectKeyword:
33473349
case SyntaxKind.BooleanKeyword:
33483350
case SyntaxKind.SymbolKeyword:
33493351
case SyntaxKind.VoidKeyword:

src/compiler/checker.ts

+11
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ namespace ts {
148148
const voidType = createIntrinsicType(TypeFlags.Void, "void");
149149
const neverType = createIntrinsicType(TypeFlags.Never, "never");
150150
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
151+
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
151152

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

@@ -4196,6 +4197,7 @@ namespace ts {
41964197
case SyntaxKind.NumberKeyword:
41974198
case SyntaxKind.BooleanKeyword:
41984199
case SyntaxKind.SymbolKeyword:
4200+
case SyntaxKind.ObjectKeyword:
41994201
case SyntaxKind.VoidKeyword:
42004202
case SyntaxKind.UndefinedKeyword:
42014203
case SyntaxKind.NullKeyword:
@@ -4780,6 +4782,7 @@ namespace ts {
47804782
t.flags & TypeFlags.NumberLike ? globalNumberType :
47814783
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
47824784
t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType() :
4785+
t.flags & TypeFlags.NonPrimitive ? globalObjectType :
47834786
t;
47844787
}
47854788

@@ -6405,6 +6408,8 @@ namespace ts {
64056408
return nullType;
64066409
case SyntaxKind.NeverKeyword:
64076410
return neverType;
6411+
case SyntaxKind.ObjectKeyword:
6412+
return nonPrimitiveType;
64086413
case SyntaxKind.JSDocNullKeyword:
64096414
return nullType;
64106415
case SyntaxKind.JSDocUndefinedKeyword:
@@ -7162,6 +7167,8 @@ namespace ts {
71627167
if (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum && isEnumTypeRelatedTo(<EnumType>source, <EnumType>target, errorReporter)) return true;
71637168
if (source.flags & TypeFlags.Undefined && (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void))) return true;
71647169
if (source.flags & TypeFlags.Null && (!strictNullChecks || target.flags & TypeFlags.Null)) return true;
7170+
if (source.flags & TypeFlags.Object && target === nonPrimitiveType) return true;
7171+
if (source.flags & TypeFlags.Primitive && target === nonPrimitiveType) return false;
71657172
if (relation === assignableRelation || relation === comparableRelation) {
71667173
if (source.flags & TypeFlags.Any) return true;
71677174
if ((source.flags & TypeFlags.Number | source.flags & TypeFlags.NumberLiteral) && target.flags & TypeFlags.EnumLike) return true;
@@ -9235,6 +9242,9 @@ namespace ts {
92359242
}
92369243

92379244
function getTypeFacts(type: Type): TypeFacts {
9245+
if (type === nonPrimitiveType) {
9246+
return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
9247+
}
92389248
const flags = type.flags;
92399249
if (flags & TypeFlags.String) {
92409250
return strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts;
@@ -18159,6 +18169,7 @@ namespace ts {
1815918169
case "string":
1816018170
case "symbol":
1816118171
case "void":
18172+
case "object":
1816218173
error(name, message, (<Identifier>name).text);
1816318174
}
1816418175
}

src/compiler/parser.ts

+3
Original file line numberDiff line numberDiff line change
@@ -2510,6 +2510,7 @@ namespace ts {
25102510
case SyntaxKind.SymbolKeyword:
25112511
case SyntaxKind.UndefinedKeyword:
25122512
case SyntaxKind.NeverKeyword:
2513+
case SyntaxKind.ObjectKeyword:
25132514
// If these are followed by a dot, then parse these out as a dotted type reference instead.
25142515
const node = tryParse(parseKeywordAndNoDot);
25152516
return node || parseTypeReference();
@@ -2568,6 +2569,7 @@ namespace ts {
25682569
case SyntaxKind.NumericLiteral:
25692570
case SyntaxKind.TrueKeyword:
25702571
case SyntaxKind.FalseKeyword:
2572+
case SyntaxKind.ObjectKeyword:
25712573
return true;
25722574
case SyntaxKind.MinusToken:
25732575
return lookAhead(nextTokenIsNumericLiteral);
@@ -6037,6 +6039,7 @@ namespace ts {
60376039
case SyntaxKind.NullKeyword:
60386040
case SyntaxKind.UndefinedKeyword:
60396041
case SyntaxKind.NeverKeyword:
6042+
case SyntaxKind.ObjectKeyword:
60406043
return parseTokenNode<JSDocType>();
60416044
case SyntaxKind.StringLiteral:
60426045
case SyntaxKind.NumericLiteral:

src/compiler/scanner.ts

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ namespace ts {
9898
"new": SyntaxKind.NewKeyword,
9999
"null": SyntaxKind.NullKeyword,
100100
"number": SyntaxKind.NumberKeyword,
101+
"object": SyntaxKind.ObjectKeyword,
101102
"package": SyntaxKind.PackageKeyword,
102103
"private": SyntaxKind.PrivateKeyword,
103104
"protected": SyntaxKind.ProtectedKeyword,

src/compiler/types.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ namespace ts {
175175
ReadonlyKeyword,
176176
RequireKeyword,
177177
NumberKeyword,
178+
ObjectKeyword,
178179
SetKeyword,
179180
StringKeyword,
180181
SymbolKeyword,
@@ -816,6 +817,7 @@ namespace ts {
816817
export interface KeywordTypeNode extends TypeNode {
817818
kind: SyntaxKind.AnyKeyword
818819
| SyntaxKind.NumberKeyword
820+
| SyntaxKind.ObjectKeyword
819821
| SyntaxKind.BooleanKeyword
820822
| SyntaxKind.StringKeyword
821823
| SyntaxKind.SymbolKeyword
@@ -2785,6 +2787,7 @@ namespace ts {
27852787
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
27862788
/* @internal */
27872789
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
2790+
NonPrimitive = 1 << 24, // intrinsic object type
27882791

27892792
/* @internal */
27902793
Nullable = Undefined | Null,
@@ -2794,7 +2797,7 @@ namespace ts {
27942797
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
27952798
PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean,
27962799
/* @internal */
2797-
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never,
2800+
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
27982801
/* @internal */
27992802
Primitive = String | Number | Boolean | Enum | ESSymbol | Void | Undefined | Null | Literal,
28002803
StringLike = String | StringLiteral | Index,
@@ -2808,8 +2811,8 @@ namespace ts {
28082811

28092812
// 'Narrowable' types are types where narrowing actually narrows.
28102813
// This *should* be every type other than null, undefined, void, and never
2811-
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol,
2812-
NotUnionOrUnit = Any | ESSymbol | Object,
2814+
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol | NonPrimitive,
2815+
NotUnionOrUnit = Any | ESSymbol | Object | NonPrimitive,
28132816
/* @internal */
28142817
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
28152818
/* @internal */
@@ -2863,6 +2866,7 @@ namespace ts {
28632866
ObjectLiteral = 1 << 7, // Originates in an object literal
28642867
EvolvingArray = 1 << 8, // Evolving array type
28652868
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
2869+
NonPrimitive = 1 << 10, // NonPrimitive object type
28662870
ClassOrInterface = Class | Interface
28672871
}
28682872

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//// [assignObjectToNonPrimitive.ts]
2+
var x = {};
3+
var y = {foo: "bar"};
4+
var a: object;
5+
a = x;
6+
a = y;
7+
8+
9+
//// [assignObjectToNonPrimitive.js]
10+
var x = {};
11+
var y = { foo: "bar" };
12+
var a;
13+
a = x;
14+
a = y;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/types/nonPrimitive/assignObjectToNonPrimitive.ts ===
2+
var x = {};
3+
>x : Symbol(x, Decl(assignObjectToNonPrimitive.ts, 0, 3))
4+
5+
var y = {foo: "bar"};
6+
>y : Symbol(y, Decl(assignObjectToNonPrimitive.ts, 1, 3))
7+
>foo : Symbol(foo, Decl(assignObjectToNonPrimitive.ts, 1, 9))
8+
9+
var a: object;
10+
>a : Symbol(a, Decl(assignObjectToNonPrimitive.ts, 2, 3))
11+
12+
a = x;
13+
>a : Symbol(a, Decl(assignObjectToNonPrimitive.ts, 2, 3))
14+
>x : Symbol(x, Decl(assignObjectToNonPrimitive.ts, 0, 3))
15+
16+
a = y;
17+
>a : Symbol(a, Decl(assignObjectToNonPrimitive.ts, 2, 3))
18+
>y : Symbol(y, Decl(assignObjectToNonPrimitive.ts, 1, 3))
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
=== tests/cases/conformance/types/nonPrimitive/assignObjectToNonPrimitive.ts ===
2+
var x = {};
3+
>x : {}
4+
>{} : {}
5+
6+
var y = {foo: "bar"};
7+
>y : { foo: string; }
8+
>{foo: "bar"} : { foo: string; }
9+
>foo : string
10+
>"bar" : "bar"
11+
12+
var a: object;
13+
>a : object
14+
15+
a = x;
16+
>a = x : {}
17+
>a : object
18+
>x : {}
19+
20+
a = y;
21+
>a = y : { foo: string; }
22+
>a : object
23+
>y : { foo: string; }
24+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAccessProperty.ts(3,3): error TS2339: Property 'nonExist' does not exist on type 'object'.
2+
3+
4+
==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAccessProperty.ts (1 errors) ====
5+
var a: object;
6+
a.toString();
7+
a.nonExist(); // error
8+
~~~~~~~~
9+
!!! error TS2339: Property 'nonExist' does not exist on type 'object'.
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [nonPrimitiveAccessProperty.ts]
2+
var a: object;
3+
a.toString();
4+
a.nonExist(); // error
5+
6+
7+
//// [nonPrimitiveAccessProperty.js]
8+
var a;
9+
a.toString();
10+
a.nonExist(); // error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAsProperty.ts(7,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'WithNonPrimitive'.
2+
Types of property 'foo' are incompatible.
3+
Type 'string' is not assignable to type 'object'.
4+
5+
6+
==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAsProperty.ts (1 errors) ====
7+
interface WithNonPrimitive {
8+
foo: object
9+
}
10+
11+
var a: WithNonPrimitive = { foo: {bar: "bar"} };
12+
13+
var b: WithNonPrimitive = {foo: "bar"}; // expect error
14+
~
15+
!!! error TS2322: Type '{ foo: string; }' is not assignable to type 'WithNonPrimitive'.
16+
!!! error TS2322: Types of property 'foo' are incompatible.
17+
!!! error TS2322: Type 'string' is not assignable to type 'object'.
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//// [nonPrimitiveAsProperty.ts]
2+
interface WithNonPrimitive {
3+
foo: object
4+
}
5+
6+
var a: WithNonPrimitive = { foo: {bar: "bar"} };
7+
8+
var b: WithNonPrimitive = {foo: "bar"}; // expect error
9+
10+
11+
//// [nonPrimitiveAsProperty.js]
12+
var a = { foo: { bar: "bar" } };
13+
var b = { foo: "bar" }; // expect error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(5,1): error TS2322: Type 'object' is not assignable to type '{ foo: string; }'.
2+
Property 'foo' is missing in type 'Object'.
3+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(13,1): error TS2322: Type 'number' is not assignable to type 'object'.
4+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(14,1): error TS2322: Type 'true' is not assignable to type 'object'.
5+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(15,1): error TS2322: Type 'string' is not assignable to type 'object'.
6+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(17,1): error TS2322: Type 'object' is not assignable to type 'number'.
7+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(18,1): error TS2322: Type 'object' is not assignable to type 'boolean'.
8+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(19,1): error TS2322: Type 'object' is not assignable to type 'string'.
9+
10+
11+
==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts (7 errors) ====
12+
var x = {};
13+
var y = {foo: "bar"};
14+
var a: object;
15+
x = a;
16+
y = a; // expect error
17+
~
18+
!!! error TS2322: Type 'object' is not assignable to type '{ foo: string; }'.
19+
!!! error TS2322: Property 'foo' is missing in type 'Object'.
20+
a = x;
21+
a = y;
22+
23+
var n = 123;
24+
var b = true;
25+
var s = "fooo";
26+
27+
a = n; // expect error
28+
~
29+
!!! error TS2322: Type 'number' is not assignable to type 'object'.
30+
a = b; // expect error
31+
~
32+
!!! error TS2322: Type 'true' is not assignable to type 'object'.
33+
a = s; // expect error
34+
~
35+
!!! error TS2322: Type 'string' is not assignable to type 'object'.
36+
37+
n = a; // expect error
38+
~
39+
!!! error TS2322: Type 'object' is not assignable to type 'number'.
40+
b = a; // expect error
41+
~
42+
!!! error TS2322: Type 'object' is not assignable to type 'boolean'.
43+
s = a; // expect error
44+
~
45+
!!! error TS2322: Type 'object' is not assignable to type 'string'.
46+
47+
var numObj: Number = 123;
48+
var boolObj: Boolean = true;
49+
var strObj: String = "string";
50+
51+
a = numObj; // ok
52+
a = boolObj; // ok
53+
a = strObj; // ok
54+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//// [nonPrimitiveAssignError.ts]
2+
var x = {};
3+
var y = {foo: "bar"};
4+
var a: object;
5+
x = a;
6+
y = a; // expect error
7+
a = x;
8+
a = y;
9+
10+
var n = 123;
11+
var b = true;
12+
var s = "fooo";
13+
14+
a = n; // expect error
15+
a = b; // expect error
16+
a = s; // expect error
17+
18+
n = a; // expect error
19+
b = a; // expect error
20+
s = a; // expect error
21+
22+
var numObj: Number = 123;
23+
var boolObj: Boolean = true;
24+
var strObj: String = "string";
25+
26+
a = numObj; // ok
27+
a = boolObj; // ok
28+
a = strObj; // ok
29+
30+
31+
//// [nonPrimitiveAssignError.js]
32+
var x = {};
33+
var y = { foo: "bar" };
34+
var a;
35+
x = a;
36+
y = a; // expect error
37+
a = x;
38+
a = y;
39+
var n = 123;
40+
var b = true;
41+
var s = "fooo";
42+
a = n; // expect error
43+
a = b; // expect error
44+
a = s; // expect error
45+
n = a; // expect error
46+
b = a; // expect error
47+
s = a; // expect error
48+
var numObj = 123;
49+
var boolObj = true;
50+
var strObj = "string";
51+
a = numObj; // ok
52+
a = boolObj; // ok
53+
a = strObj; // ok

0 commit comments

Comments
 (0)