From 251723826962cc3fa3cc2d766c17dc197099cce3 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 2 Jun 2016 06:32:14 -0700
Subject: [PATCH 1/3] Add non-widening forms of null and undefined

---
 src/compiler/checker.ts | 37 ++++++++++++++++++++-----------------
 src/compiler/types.ts   |  6 +++---
 2 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 06ef246340252..6166234dcf4d3 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -110,16 +110,17 @@ namespace ts {
         const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
         const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
 
-        const nullableWideningFlags = strictNullChecks ? 0 : TypeFlags.ContainsUndefinedOrNull;
         const anyType = createIntrinsicType(TypeFlags.Any, "any");
         const stringType = createIntrinsicType(TypeFlags.String, "string");
         const numberType = createIntrinsicType(TypeFlags.Number, "number");
         const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
         const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
         const voidType = createIntrinsicType(TypeFlags.Void, "void");
-        const undefinedType = createIntrinsicType(TypeFlags.Undefined | nullableWideningFlags, "undefined");
-        const nullType = createIntrinsicType(TypeFlags.Null | nullableWideningFlags, "null");
-        const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
+        const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
+        const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
+        const nullType = createIntrinsicType(TypeFlags.Null, "null");
+        const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
+        const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
         const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
         const neverType = createIntrinsicType(TypeFlags.Never, "never");
 
@@ -3405,7 +3406,7 @@ namespace ts {
                     error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol));
                     return type.resolvedBaseConstructorType = unknownType;
                 }
-                if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) {
+                if (baseConstructorType !== unknownType && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) {
                     error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType));
                     return type.resolvedBaseConstructorType = unknownType;
                 }
@@ -5011,6 +5012,7 @@ namespace ts {
             containsAny?: boolean;
             containsUndefined?: boolean;
             containsNull?: boolean;
+            containsNonWideningType?: boolean;
         }
 
         function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) {
@@ -5021,6 +5023,7 @@ namespace ts {
                 if (type.flags & TypeFlags.Any) typeSet.containsAny = true;
                 if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true;
                 if (type.flags & TypeFlags.Null) typeSet.containsNull = true;
+                if (!(type.flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true;
             }
             else if (type !== neverType && !contains(typeSet, type)) {
                 typeSet.push(type);
@@ -5081,8 +5084,8 @@ namespace ts {
                 removeSubtypes(typeSet);
             }
             if (typeSet.length === 0) {
-                return typeSet.containsNull ? nullType :
-                    typeSet.containsUndefined ? undefinedType :
+                return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType :
+                    typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType :
                     neverType;
             }
             else if (typeSet.length === 1) {
@@ -5882,7 +5885,7 @@ namespace ts {
                 if (!(target.flags & TypeFlags.Never)) {
                     if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return Ternary.True;
                     if (source.flags & TypeFlags.Undefined) {
-                        if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void) || source === emptyArrayElementType) return Ternary.True;
+                        if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void)) return Ternary.True;
                     }
                     if (source.flags & TypeFlags.Null) {
                         if (!strictNullChecks || target.flags & TypeFlags.Null) return Ternary.True;
@@ -6972,7 +6975,7 @@ namespace ts {
             if (type.flags & TypeFlags.ObjectLiteral) {
                 for (const p of getPropertiesOfObjectType(type)) {
                     const t = getTypeOfSymbol(p);
-                    if (t.flags & TypeFlags.ContainsUndefinedOrNull) {
+                    if (t.flags & TypeFlags.ContainsWideningType) {
                         if (!reportWideningErrorsInType(t)) {
                             error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
                         }
@@ -7019,7 +7022,7 @@ namespace ts {
         }
 
         function reportErrorsFromWidening(declaration: Declaration, type: Type) {
-            if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) {
+            if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsWideningType) {
                 // Report implicit any error within type if possible, otherwise report error on declaration
                 if (!reportWideningErrorsInType(type)) {
                     reportImplicitAnyError(declaration, type);
@@ -8311,7 +8314,7 @@ namespace ts {
             const classInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(classSymbol);
             const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType);
 
-            return baseConstructorType === nullType;
+            return baseConstructorType === nullWideningType;
         }
 
         function checkThisExpression(node: Node): Type {
@@ -9202,7 +9205,7 @@ namespace ts {
                     }
                 }
             }
-            return createArrayType(elementTypes.length ? getUnionType(elementTypes) : emptyArrayElementType);
+            return createArrayType(elementTypes.length ? getUnionType(elementTypes) : strictNullChecks ? neverType : undefinedWideningType);
         }
 
         function isNumericName(name: DeclarationName): boolean {
@@ -12002,7 +12005,7 @@ namespace ts {
 
         function checkVoidExpression(node: VoidExpression): Type {
             checkExpression(node.expression);
-            return undefinedType;
+            return undefinedWideningType;
         }
 
         function checkAwaitExpression(node: AwaitExpression): Type {
@@ -12409,7 +12412,7 @@ namespace ts {
                 case SyntaxKind.InKeyword:
                     return checkInExpression(left, right, leftType, rightType);
                 case SyntaxKind.AmpersandAmpersandToken:
-                    return addNullableKind(rightType, getNullableKind(leftType));
+                    return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType;
                 case SyntaxKind.BarBarToken:
                     return getUnionType([getNonNullableType(leftType), rightType]);
                 case SyntaxKind.EqualsToken:
@@ -12676,7 +12679,7 @@ namespace ts {
                 case SyntaxKind.SuperKeyword:
                     return checkSuperExpression(node);
                 case SyntaxKind.NullKeyword:
-                    return nullType;
+                    return nullWideningType;
                 case SyntaxKind.TrueKeyword:
                 case SyntaxKind.FalseKeyword:
                     return booleanType;
@@ -12734,7 +12737,7 @@ namespace ts {
                 case SyntaxKind.SpreadElementExpression:
                     return checkSpreadElementExpression(<SpreadElementExpression>node, contextualMapper);
                 case SyntaxKind.OmittedExpression:
-                    return undefinedType;
+                    return undefinedWideningType;
                 case SyntaxKind.YieldExpression:
                     return checkYieldExpression(<YieldExpression>node);
                 case SyntaxKind.JsxExpression:
@@ -17648,7 +17651,7 @@ namespace ts {
             // Setup global builtins
             addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0);
 
-            getSymbolLinks(undefinedSymbol).type = undefinedType;
+            getSymbolLinks(undefinedSymbol).type = undefinedWideningType;
             getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments");
             getSymbolLinks(unknownSymbol).type = unknownType;
 
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index c7e14a1003108..4c0e7a036dbf4 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2197,7 +2197,7 @@ namespace ts {
         /* @internal */
         FreshObjectLiteral      = 0x00100000,  // Fresh object literal type
         /* @internal */
-        ContainsUndefinedOrNull = 0x00200000,  // Type is or contains undefined or null type
+        ContainsWideningType    = 0x00200000,  // Type is or contains undefined or null widening type
         /* @internal */
         ContainsObjectLiteral   = 0x00400000,  // Type is or contains object literal type
         /* @internal */
@@ -2220,9 +2220,9 @@ namespace ts {
         StructuredType = ObjectType | Union | Intersection,
         Narrowable = Any | ObjectType | Union | TypeParameter,
         /* @internal */
-        RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral,
+        RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
         /* @internal */
-        PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType
+        PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType
     }
 
     export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;

From 20bab14224bf9f7a7228f416a61807b1f7f73e4a Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 2 Jun 2016 09:39:47 -0700
Subject: [PATCH 2/3] Add tests

---
 .../reference/arrayLiteralWidened.js          | 15 ++++
 .../reference/arrayLiteralWidened.symbols     | 38 +++++++--
 .../reference/arrayLiteralWidened.types       | 29 +++++++
 .../reference/initializersWidened.js          | 42 +++++++++-
 .../reference/initializersWidened.symbols     | 55 ++++++++++++-
 .../reference/initializersWidened.types       | 77 ++++++++++++++++++-
 .../logicalAndOperatorWithEveryType.types     |  2 +-
 .../reference/objectLiteralWidened.js         | 40 +++++++++-
 .../reference/objectLiteralWidened.symbols    | 53 +++++++++++--
 .../reference/objectLiteralWidened.types      | 54 ++++++++++++-
 .../reference/strictNullChecksNoWidening.js   | 31 ++++++++
 .../strictNullChecksNoWidening.symbols        | 48 ++++++++++++
 .../strictNullChecksNoWidening.types          | 67 ++++++++++++++++
 .../widenedTypes/arrayLiteralWidened.ts       |  9 +++
 .../widenedTypes/initializersWidened.ts       | 24 +++++-
 .../widenedTypes/objectLiteralWidened.ts      | 22 +++++-
 .../strictNullChecksNoWidening.ts             | 17 ++++
 17 files changed, 584 insertions(+), 39 deletions(-)
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.js
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.symbols
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.types
 create mode 100644 tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts

diff --git a/tests/baselines/reference/arrayLiteralWidened.js b/tests/baselines/reference/arrayLiteralWidened.js
index a250c47849d57..5b7346b87381b 100644
--- a/tests/baselines/reference/arrayLiteralWidened.js
+++ b/tests/baselines/reference/arrayLiteralWidened.js
@@ -2,6 +2,7 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
+var a = [,,];
 
 var a = [null, null];
 var a = [undefined, undefined];
@@ -12,11 +13,20 @@ var b = [[undefined, undefined]];
 
 var c = [[[]]]; // any[][][]
 var c = [[[null]],[undefined]]
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
 
 
 //// [arrayLiteralWidened.js]
 // array literals are widened upon assignment according to their element type
 var a = []; // any[]
+var a = [, ,];
 var a = [null, null];
 var a = [undefined, undefined];
 var b = [[], [null, null]]; // any[][]
@@ -24,3 +34,8 @@ var b = [[], []];
 var b = [[undefined, undefined]];
 var c = [[[]]]; // any[][][]
 var c = [[[null]], [undefined]];
+// no widening when one or more elements are non-widening
+var x = undefined;
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
diff --git a/tests/baselines/reference/arrayLiteralWidened.symbols b/tests/baselines/reference/arrayLiteralWidened.symbols
index ba058aeded114..48137812a5dfc 100644
--- a/tests/baselines/reference/arrayLiteralWidened.symbols
+++ b/tests/baselines/reference/arrayLiteralWidened.symbols
@@ -2,31 +2,53 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
+
+var a = [,,];
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 
 var a = [null, null];
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 
 var a = [undefined, undefined];
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 >undefined : Symbol(undefined)
 >undefined : Symbol(undefined)
 
 var b = [[], [null, null]]; // any[][]
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 
 var b = [[], []];
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 
 var b = [[undefined, undefined]];
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 >undefined : Symbol(undefined)
 >undefined : Symbol(undefined)
 
 var c = [[[]]]; // any[][][]
->c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
+>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
 
 var c = [[[null]],[undefined]]
->c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
+>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
+>undefined : Symbol(undefined)
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+>undefined : Symbol(undefined)
+
+var d = [x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+
+var d = [, x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+
+var d = [undefined, x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
 >undefined : Symbol(undefined)
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
 
diff --git a/tests/baselines/reference/arrayLiteralWidened.types b/tests/baselines/reference/arrayLiteralWidened.types
index 83db7046eed4a..6237cd79a7edb 100644
--- a/tests/baselines/reference/arrayLiteralWidened.types
+++ b/tests/baselines/reference/arrayLiteralWidened.types
@@ -5,6 +5,12 @@ var a = []; // any[]
 >a : any[]
 >[] : undefined[]
 
+var a = [,,];
+>a : any[]
+>[,,] : undefined[]
+> : undefined
+> : undefined
+
 var a = [null, null];
 >a : any[]
 >[null, null] : null[]
@@ -53,3 +59,26 @@ var c = [[[null]],[undefined]]
 >[undefined] : undefined[]
 >undefined : undefined
 
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+>x : undefined
+>undefined : undefined
+
+var d = [x];
+>d : undefined[]
+>[x] : undefined[]
+>x : undefined
+
+var d = [, x];
+>d : undefined[]
+>[, x] : undefined[]
+> : undefined
+>x : undefined
+
+var d = [undefined, x];
+>d : undefined[]
+>[undefined, x] : undefined[]
+>undefined : undefined
+>x : undefined
+
diff --git a/tests/baselines/reference/initializersWidened.js b/tests/baselines/reference/initializersWidened.js
index fce0750a600c8..3954735ee6ede 100644
--- a/tests/baselines/reference/initializersWidened.js
+++ b/tests/baselines/reference/initializersWidened.js
@@ -1,10 +1,44 @@
 //// [initializersWidened.ts]
 // these are widened to any at the point of assignment
 
-var x = null;
-var y = undefined;
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+
+// these are not widened
+
+var x2: null;
+var y2: undefined;
+
+var x3: null = null;
+var y3: undefined = undefined;
+var z3: undefined = void 0;
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
 
 //// [initializersWidened.js]
 // these are widened to any at the point of assignment
-var x = null;
-var y = undefined;
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+// these are not widened
+var x2;
+var y2;
+var x3 = null;
+var y3 = undefined;
+var z3 = void 0;
+// widen only when all constituents of union are widening
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
diff --git a/tests/baselines/reference/initializersWidened.symbols b/tests/baselines/reference/initializersWidened.symbols
index 625c066a0587e..252a248bec867 100644
--- a/tests/baselines/reference/initializersWidened.symbols
+++ b/tests/baselines/reference/initializersWidened.symbols
@@ -1,10 +1,57 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
 // these are widened to any at the point of assignment
 
-var x = null;
->x : Symbol(x, Decl(initializersWidened.ts, 2, 3))
+var x1 = null;
+>x1 : Symbol(x1, Decl(initializersWidened.ts, 2, 3))
 
-var y = undefined;
->y : Symbol(y, Decl(initializersWidened.ts, 3, 3))
+var y1 = undefined;
+>y1 : Symbol(y1, Decl(initializersWidened.ts, 3, 3))
 >undefined : Symbol(undefined)
 
+var z1 = void 0;
+>z1 : Symbol(z1, Decl(initializersWidened.ts, 4, 3))
+
+// these are not widened
+
+var x2: null;
+>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
+
+var y2: undefined;
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
+var x3: null = null;
+>x3 : Symbol(x3, Decl(initializersWidened.ts, 11, 3))
+
+var y3: undefined = undefined;
+>y3 : Symbol(y3, Decl(initializersWidened.ts, 12, 3))
+>undefined : Symbol(undefined)
+
+var z3: undefined = void 0;
+>z3 : Symbol(z3, Decl(initializersWidened.ts, 13, 3))
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+>x4 : Symbol(x4, Decl(initializersWidened.ts, 17, 3))
+
+var y4 = undefined || undefined;
+>y4 : Symbol(y4, Decl(initializersWidened.ts, 18, 3))
+>undefined : Symbol(undefined)
+>undefined : Symbol(undefined)
+
+var z4 = void 0 || void 0;
+>z4 : Symbol(z4, Decl(initializersWidened.ts, 19, 3))
+
+var x5 = null || x2;
+>x5 : Symbol(x5, Decl(initializersWidened.ts, 21, 3))
+>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
+
+var y5 = undefined || y2;
+>y5 : Symbol(y5, Decl(initializersWidened.ts, 22, 3))
+>undefined : Symbol(undefined)
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
+var z5 = void 0 || y2;
+>z5 : Symbol(z5, Decl(initializersWidened.ts, 23, 3))
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
diff --git a/tests/baselines/reference/initializersWidened.types b/tests/baselines/reference/initializersWidened.types
index 157055892460a..766f859029af9 100644
--- a/tests/baselines/reference/initializersWidened.types
+++ b/tests/baselines/reference/initializersWidened.types
@@ -1,11 +1,80 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
 // these are widened to any at the point of assignment
 
-var x = null;
->x : any
+var x1 = null;
+>x1 : any
 >null : null
 
-var y = undefined;
->y : any
+var y1 = undefined;
+>y1 : any
 >undefined : undefined
 
+var z1 = void 0;
+>z1 : any
+>void 0 : undefined
+>0 : number
+
+// these are not widened
+
+var x2: null;
+>x2 : null
+>null : null
+
+var y2: undefined;
+>y2 : undefined
+
+var x3: null = null;
+>x3 : null
+>null : null
+>null : null
+
+var y3: undefined = undefined;
+>y3 : undefined
+>undefined : undefined
+
+var z3: undefined = void 0;
+>z3 : undefined
+>void 0 : undefined
+>0 : number
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+>x4 : any
+>null || null : null
+>null : null
+>null : null
+
+var y4 = undefined || undefined;
+>y4 : any
+>undefined || undefined : undefined
+>undefined : undefined
+>undefined : undefined
+
+var z4 = void 0 || void 0;
+>z4 : any
+>void 0 || void 0 : undefined
+>void 0 : undefined
+>0 : number
+>void 0 : undefined
+>0 : number
+
+var x5 = null || x2;
+>x5 : null
+>null || x2 : null
+>null : null
+>x2 : null
+
+var y5 = undefined || y2;
+>y5 : undefined
+>undefined || y2 : undefined
+>undefined : undefined
+>y2 : undefined
+
+var z5 = void 0 || y2;
+>z5 : undefined
+>void 0 || y2 : undefined
+>void 0 : undefined
+>0 : number
+>y2 : undefined
+
diff --git a/tests/baselines/reference/logicalAndOperatorWithEveryType.types b/tests/baselines/reference/logicalAndOperatorWithEveryType.types
index b2628eed9219f..bd913e94da58c 100644
--- a/tests/baselines/reference/logicalAndOperatorWithEveryType.types
+++ b/tests/baselines/reference/logicalAndOperatorWithEveryType.types
@@ -623,7 +623,7 @@ var rj8 = a8 && undefined;
 
 var rj9 = null && undefined;
 >rj9 : any
->null && undefined : null
+>null && undefined : undefined
 >null : null
 >undefined : undefined
 
diff --git a/tests/baselines/reference/objectLiteralWidened.js b/tests/baselines/reference/objectLiteralWidened.js
index 4de228cb14a7f..98d79133540bd 100644
--- a/tests/baselines/reference/objectLiteralWidened.js
+++ b/tests/baselines/reference/objectLiteralWidened.js
@@ -1,29 +1,61 @@
 //// [objectLiteralWidened.ts]
 // object literal properties are widened to any
 
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 }
 
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
+}
+
+// these are not widened
+
+var u: undefined = undefined;
+var n: null = null;
+
+var x2 = {
+    foo: n,
+    bar: u
+}
+
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
 }
 
 //// [objectLiteralWidened.js]
 // object literal properties are widened to any
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 };
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
 };
+// these are not widened
+var u = undefined;
+var n = null;
+var x2 = {
+    foo: n,
+    bar: u
+};
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
+};
diff --git a/tests/baselines/reference/objectLiteralWidened.symbols b/tests/baselines/reference/objectLiteralWidened.symbols
index 0bf077cd9d8a1..4b2a1b4a00185 100644
--- a/tests/baselines/reference/objectLiteralWidened.symbols
+++ b/tests/baselines/reference/objectLiteralWidened.symbols
@@ -1,22 +1,22 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
 // object literal properties are widened to any
 
-var x = {
->x : Symbol(x, Decl(objectLiteralWidened.ts, 2, 3))
+var x1 = {
+>x1 : Symbol(x1, Decl(objectLiteralWidened.ts, 2, 3))
 
     foo: null,
->foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 9))
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 10))
 
     bar: undefined
 >bar : Symbol(bar, Decl(objectLiteralWidened.ts, 3, 14))
 >undefined : Symbol(undefined)
 }
 
-var y = {
->y : Symbol(y, Decl(objectLiteralWidened.ts, 7, 3))
+var y1 = {
+>y1 : Symbol(y1, Decl(objectLiteralWidened.ts, 7, 3))
 
     foo: null,
->foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 9))
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 10))
 
     bar: {
 >bar : Symbol(bar, Decl(objectLiteralWidened.ts, 8, 14))
@@ -29,3 +29,44 @@ var y = {
 >undefined : Symbol(undefined)
     }
 }
+
+// these are not widened
+
+var u: undefined = undefined;
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+>undefined : Symbol(undefined)
+
+var n: null = null;
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+var x2 = {
+>x2 : Symbol(x2, Decl(objectLiteralWidened.ts, 20, 3))
+
+    foo: n,
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 20, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+    bar: u
+>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 21, 11))
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+}
+
+var y2 = {
+>y2 : Symbol(y2, Decl(objectLiteralWidened.ts, 25, 3))
+
+    foo: n,
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 25, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+    bar: {
+>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 26, 11))
+
+        baz: n,
+>baz : Symbol(baz, Decl(objectLiteralWidened.ts, 27, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+        boo: u
+>boo : Symbol(boo, Decl(objectLiteralWidened.ts, 28, 15))
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+    }
+}
diff --git a/tests/baselines/reference/objectLiteralWidened.types b/tests/baselines/reference/objectLiteralWidened.types
index 9f47e47795d56..66309202617d9 100644
--- a/tests/baselines/reference/objectLiteralWidened.types
+++ b/tests/baselines/reference/objectLiteralWidened.types
@@ -1,8 +1,8 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
 // object literal properties are widened to any
 
-var x = {
->x : { foo: any; bar: any; }
+var x1 = {
+>x1 : { foo: any; bar: any; }
 >{    foo: null,    bar: undefined} : { foo: null; bar: undefined; }
 
     foo: null,
@@ -14,8 +14,8 @@ var x = {
 >undefined : undefined
 }
 
-var y = {
->y : { foo: any; bar: { baz: any; boo: any; }; }
+var y1 = {
+>y1 : { foo: any; bar: { baz: any; boo: any; }; }
 >{    foo: null,    bar: {        baz: null,        boo: undefined    }} : { foo: null; bar: { baz: null; boo: undefined; }; }
 
     foo: null,
@@ -35,3 +35,49 @@ var y = {
 >undefined : undefined
     }
 }
+
+// these are not widened
+
+var u: undefined = undefined;
+>u : undefined
+>undefined : undefined
+
+var n: null = null;
+>n : null
+>null : null
+>null : null
+
+var x2 = {
+>x2 : { foo: null; bar: undefined; }
+>{    foo: n,    bar: u} : { foo: null; bar: undefined; }
+
+    foo: n,
+>foo : null
+>n : null
+
+    bar: u
+>bar : undefined
+>u : undefined
+}
+
+var y2 = {
+>y2 : { foo: null; bar: { baz: null; boo: undefined; }; }
+>{    foo: n,    bar: {        baz: n,        boo: u    }} : { foo: null; bar: { baz: null; boo: undefined; }; }
+
+    foo: n,
+>foo : null
+>n : null
+
+    bar: {
+>bar : { baz: null; boo: undefined; }
+>{        baz: n,        boo: u    } : { baz: null; boo: undefined; }
+
+        baz: n,
+>baz : null
+>n : null
+
+        boo: u
+>boo : undefined
+>u : undefined
+    }
+}
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.js b/tests/baselines/reference/strictNullChecksNoWidening.js
new file mode 100644
index 0000000000000..ba26a04fc131a
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.js
@@ -0,0 +1,31 @@
+//// [strictNullChecksNoWidening.ts]
+
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+
+declare function f<T>(x: T): T;
+
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);
+
+
+//// [strictNullChecksNoWidening.js]
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.symbols b/tests/baselines/reference/strictNullChecksNoWidening.symbols
new file mode 100644
index 0000000000000..a23a0d2e926c0
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.symbols
@@ -0,0 +1,48 @@
+=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
+
+var a1 = null;
+>a1 : Symbol(a1, Decl(strictNullChecksNoWidening.ts, 1, 3))
+
+var a2 = undefined;
+>a2 : Symbol(a2, Decl(strictNullChecksNoWidening.ts, 2, 3))
+>undefined : Symbol(undefined)
+
+var a3 = void 0;
+>a3 : Symbol(a3, Decl(strictNullChecksNoWidening.ts, 3, 3))
+
+var b1 = [];
+>b1 : Symbol(b1, Decl(strictNullChecksNoWidening.ts, 5, 3))
+
+var b2 = [,];
+>b2 : Symbol(b2, Decl(strictNullChecksNoWidening.ts, 6, 3))
+
+var b3 = [undefined];
+>b3 : Symbol(b3, Decl(strictNullChecksNoWidening.ts, 7, 3))
+>undefined : Symbol(undefined)
+
+var b4 = [[], []];
+>b4 : Symbol(b4, Decl(strictNullChecksNoWidening.ts, 8, 3))
+
+var b5 = [[], [,]];
+>b5 : Symbol(b5, Decl(strictNullChecksNoWidening.ts, 9, 3))
+
+declare function f<T>(x: T): T;
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+>x : Symbol(x, Decl(strictNullChecksNoWidening.ts, 11, 22))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+
+var c1 = f(null);
+>c1 : Symbol(c1, Decl(strictNullChecksNoWidening.ts, 13, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+
+var c2 = f(undefined);
+>c2 : Symbol(c2, Decl(strictNullChecksNoWidening.ts, 14, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+>undefined : Symbol(undefined)
+
+var c3 = f([]);
+>c3 : Symbol(c3, Decl(strictNullChecksNoWidening.ts, 15, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.types b/tests/baselines/reference/strictNullChecksNoWidening.types
new file mode 100644
index 0000000000000..6dd2ee3fb4cfb
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.types
@@ -0,0 +1,67 @@
+=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
+
+var a1 = null;
+>a1 : null
+>null : null
+
+var a2 = undefined;
+>a2 : undefined
+>undefined : undefined
+
+var a3 = void 0;
+>a3 : undefined
+>void 0 : undefined
+>0 : number
+
+var b1 = [];
+>b1 : never[]
+>[] : never[]
+
+var b2 = [,];
+>b2 : undefined[]
+>[,] : undefined[]
+> : undefined
+
+var b3 = [undefined];
+>b3 : undefined[]
+>[undefined] : undefined[]
+>undefined : undefined
+
+var b4 = [[], []];
+>b4 : never[][]
+>[[], []] : never[][]
+>[] : never[]
+>[] : never[]
+
+var b5 = [[], [,]];
+>b5 : undefined[][]
+>[[], [,]] : undefined[][]
+>[] : never[]
+>[,] : undefined[]
+> : undefined
+
+declare function f<T>(x: T): T;
+>f : <T>(x: T) => T
+>T : T
+>x : T
+>T : T
+>T : T
+
+var c1 = f(null);
+>c1 : null
+>f(null) : null
+>f : <T>(x: T) => T
+>null : null
+
+var c2 = f(undefined);
+>c2 : undefined
+>f(undefined) : undefined
+>f : <T>(x: T) => T
+>undefined : undefined
+
+var c3 = f([]);
+>c3 : never[]
+>f([]) : never[]
+>f : <T>(x: T) => T
+>[] : never[]
+
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
index 8af0a5842cc2e..05428422129c6 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
@@ -1,6 +1,7 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
+var a = [,,];
 
 var a = [null, null];
 var a = [undefined, undefined];
@@ -11,3 +12,11 @@ var b = [[undefined, undefined]];
 
 var c = [[[]]]; // any[][][]
 var c = [[[null]],[undefined]]
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
index e79cdc9e16834..2eeb96194b753 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
@@ -1,4 +1,24 @@
 // these are widened to any at the point of assignment
 
-var x = null;
-var y = undefined;
\ No newline at end of file
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+
+// these are not widened
+
+var x2: null;
+var y2: undefined;
+
+var x3: null = null;
+var y3: undefined = undefined;
+var z3: undefined = void 0;
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
\ No newline at end of file
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
index cde44f9116856..8b51e526882e7 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
@@ -1,14 +1,32 @@
 // object literal properties are widened to any
 
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 }
 
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
+}
+
+// these are not widened
+
+var u: undefined = undefined;
+var n: null = null;
+
+var x2 = {
+    foo: n,
+    bar: u
+}
+
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
 }
\ No newline at end of file
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts
new file mode 100644
index 0000000000000..8f5b4709abf9e
--- /dev/null
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts
@@ -0,0 +1,17 @@
+// @strictNullChecks: true
+
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+
+declare function f<T>(x: T): T;
+
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);

From fb2df77a59453e72e3bbc92ab4bb9e5c68cb4a8a Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 2 Jun 2016 10:47:47 -0700
Subject: [PATCH 3/3] Remove unused variable

---
 src/compiler/checker.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 6166234dcf4d3..b8f078ea9a56b 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -120,7 +120,6 @@ namespace ts {
         const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
         const nullType = createIntrinsicType(TypeFlags.Null, "null");
         const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
-        const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
         const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
         const neverType = createIntrinsicType(TypeFlags.Never, "never");