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

Preserve literal types in contextual unions #19966

Merged
merged 14 commits into from
Dec 11, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
35 changes: 21 additions & 14 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4306,9 +4306,10 @@ namespace ts {
if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return declaration.initializer ?
type = declaration.initializer ?
getUnionType([type, checkExpressionCached(declaration.initializer)], /*subtypeReduction*/ true) :
type;
return shouldUseLiteralType(declaration) ? type : getWidenedLiteralType(type);
Copy link
Member

Choose a reason for hiding this comment

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

What are the cases that are affected by this change?

Copy link
Member Author

Choose a reason for hiding this comment

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

}

function getTypeForDeclarationFromJSDocComment(declaration: Node) {
Expand Down Expand Up @@ -10672,7 +10673,7 @@ namespace ts {
}

function getWidenedLiteralLikeTypeForContextualType(type: Type, contextualType: Type) {
if (!isLiteralLikeContextualType(contextualType)) {
if (!isLiteralLikeContextualType(type, contextualType)) {
type = getWidenedUniqueESSymbolType(getWidenedLiteralType(type));
}
return type;
Expand Down Expand Up @@ -18856,24 +18857,30 @@ namespace ts {

function checkDeclarationInitializer(declaration: VariableLikeDeclaration) {
const type = getTypeOfExpression(declaration.initializer, /*cache*/ true);
return shouldUseLiteralType(declaration) ? type : getWidenedLiteralType(type);
}

function shouldUseLiteralType(declaration: VariableLikeDeclaration) {
Copy link
Member

Choose a reason for hiding this comment

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

shouldKeepLiteralType may be a better name since it only matters if you already have a literal type.

return getCombinedNodeFlags(declaration) & NodeFlags.Const ||
(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration)) ||
isTypeAssertion(declaration.initializer) ? type : getWidenedLiteralType(type);
getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration) ||
(declaration.initializer && isTypeAssertion(declaration.initializer));
}

function isLiteralLikeContextualType(contextualType: Type) {
function isLiteralLikeContextualType(candidateLiteral: Type, contextualType: Type): boolean {
Copy link
Member

Choose a reason for hiding this comment

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

isLiteralOfContextualType

if (contextualType) {
if (contextualType.flags & TypeFlags.UnionOrIntersection) {
return some((contextualType as UnionOrIntersectionType).types, t => isLiteralLikeContextualType(candidateLiteral, t));
}
if (contextualType.flags & TypeFlags.TypeVariable) {
const constraint = getBaseConstraintOfType(contextualType) || emptyObjectType;
// If the type parameter is constrained to the base primitive type we're checking for,
// consider this a literal context. For example, given a type parameter 'T extends string',
// this causes us to infer string literal types for T.
if (constraint.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean | TypeFlags.Enum | TypeFlags.ESSymbol)) {
return true;
}
contextualType = constraint;
}
return maybeTypeOfKind(contextualType, (TypeFlags.Literal | TypeFlags.Index | TypeFlags.UniqueESSymbol));
return isLiteralLikeContextualType(candidateLiteral, constraint);
}
// No need to `maybeTypeOfKind` on the contextual type, as it can't be a union, _however_, `candidateLiteral` might still be one!
return !!(((contextualType.flags & TypeFlags.StringLike) && maybeTypeOfKind(candidateLiteral, TypeFlags.StringLike)) ||
((contextualType.flags & TypeFlags.NumberLike) && maybeTypeOfKind(candidateLiteral, TypeFlags.NumberLike)) ||
((contextualType.flags & TypeFlags.BooleanLike) && maybeTypeOfKind(candidateLiteral, TypeFlags.BooleanLike)) ||
((contextualType.flags & TypeFlags.ESSymbolLike) && maybeTypeOfKind(candidateLiteral, TypeFlags.ESSymbolLike))
Copy link
Member

Choose a reason for hiding this comment

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

Why all the extra parentheses?

Copy link
Member Author

Choose a reason for hiding this comment

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

Old habit; I tend to add parens around any inner binary expression since internalizing operator precedence beyond simple PEMDAS isn't something I, personally, bother to think about. They're gone now~

);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Point {
static Origin(): Point { return { x: 0, y: 0 }; } // unexpected error here bug 840246
>Origin : () => Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down Expand Up @@ -38,7 +38,7 @@ module A {
static Origin(): Point { return { x: 0, y: 0 }; } // unexpected error here bug 840246
>Origin : () => Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Point {
static Origin(): Point { return { x: 0, y: 0 }; }
>Origin : () => Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down Expand Up @@ -38,7 +38,7 @@ module A {
static Origin(): Point { return { x: 0, y: 0 }; }
>Origin : () => Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Point {
static Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down Expand Up @@ -38,7 +38,7 @@ module A {
static Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Point {
static Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down Expand Up @@ -38,7 +38,7 @@ module A {
static Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/ES5For-of30.types
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var a: string, b: number;

var tuple: [number, string] = [2, "3"];
>tuple : [number, string]
>[2, "3"] : [number, string]
>[2, "3"] : [2, "3"]
>2 : 2
>"3" : "3"

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/ES5For-ofTypeCheck3.types
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck3.ts ===
var tuple: [string, number] = ["", 0];
>tuple : [string, number]
>["", 0] : [string, number]
>["", 0] : ["", 0]
>"" : ""
>0 : 0

Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/ES5for-of32.types
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ for (let num of array) {
>0 : 0

array = [4,5,6]
>array = [4,5,6] : number[]
>array = [4,5,6] : (4 | 5 | 6)[]
>array : number[]
>[4,5,6] : number[]
>[4,5,6] : (4 | 5 | 6)[]
>4 : 4
>5 : 5
>6 : 6
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand All @@ -32,7 +32,7 @@ module A {
export var Origin3d: Point3d = { x: 0, y: 0, z: 0 };
>Origin3d : Point3d
>Point3d : Point3d
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
>{ x: 0, y: 0, z: 0 } : { x: 0; y: 0; z: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand All @@ -32,7 +32,7 @@ module A {
export var Origin3d: Point3d = { x: 0, y: 0, z: 0 };
>Origin3d : Point3d
>Point3d : Point3d
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
>{ x: 0, y: 0, z: 0 } : { x: 0; y: 0; z: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module A {
return new Line({ x: 0, y: 0 }, p);
>new Line({ x: 0, y: 0 }, p) : Line
>Line : typeof Line
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module A {
return new Line({ x: 0, y: 0 }, p);
>new Line({ x: 0, y: 0 }, p) : Line
>Line : typeof Line
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module A {
return new Line({ x: 0, y: 0 }, p);
>new Line({ x: 0, y: 0 }, p) : Line
>Line : typeof Line
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand All @@ -32,7 +32,7 @@ module A {
export var Origin3d: Point3d = { x: 0, y: 0, z: 0 };
>Origin3d : Point3d
>Point3d : Point3d
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
>{ x: 0, y: 0, z: 0 } : { x: 0; y: 0; z: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand All @@ -32,7 +32,7 @@ module A {
export var Origin3d: Point3d = { x: 0, y: 0, z: 0 };
>Origin3d : Point3d
>Point3d : Point3d
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
>{ x: 0, y: 0, z: 0 } : { x: 0; y: 0; z: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module A {
return new Line({ x: 0, y: 0 }, p);
>new Line({ x: 0, y: 0 }, p) : Line
>Line : typeof Line
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand All @@ -34,7 +34,7 @@ module A {
export var Origin3d: Point3d = { x: 0, y: 0, z: 0 };
>Origin3d : Point3d
>Point3d : Point3d
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
>{ x: 0, y: 0, z: 0 } : { x: 0; y: 0; z: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module Geometry {
>Origin : Points.Point
>Points : any
>Point : Points.Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand All @@ -68,7 +68,7 @@ module Geometry {
>Lines : typeof Lines
>Line : typeof Lines.Line
>Origin : Points.Point
>{ x: 1, y: 0 } : { x: number; y: number; }
>{ x: 1, y: 0 } : { x: 1; y: 0; }
>x : number
>1 : 1
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down Expand Up @@ -121,7 +121,7 @@ var p = new A.Utils.Plane(o, { x: 1, y: 1 });
>Utils : typeof A.Utils
>Plane : typeof A.Utils.Plane
>o : { x: number; y: number; }
>{ x: 1, y: 1 } : { x: number; y: number; }
>{ x: 1, y: 1 } : { x: 1; y: 1; }
>x : number
>1 : 1
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module otherRoot {
>Root : any
>A : any
>Point : Root.A.Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module A {
export var Origin: Point = { x: 0, y: 0 };
>Origin : Point
>Point : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>{ x: 0, y: 0 } : { x: 0; y: 0; }
>x : number
>0 : 0
>y : number
Expand Down Expand Up @@ -117,7 +117,7 @@ var p = new A.Utils.Plane(o, { x: 1, y: 1 });
>Utils : typeof A.Utils
>Plane : typeof A.Utils.Plane
>o : { x: number; y: number; }
>{ x: 1, y: 1 } : { x: number; y: number; }
>{ x: 1, y: 1 } : { x: 1; y: 1; }
>x : number
>1 : 1
>y : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class C {
>x : (a: string) => string

return (x: string) => "";
>(x: string) => "" : (x: string) => string
>(x: string) => "" : (x: string) => ""
>x : string
>"" : ""
}
Expand Down
Loading