Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e836fe1
Initial implementation of Union Types
ahejlsberg Oct 4, 2014
d70494f
Narrowing of variable types using typeof/instanceof type guards
ahejlsberg Oct 7, 2014
b8923b3
Support symbol kind for union properties
mhegazy Oct 8, 2014
5669f63
add test for quick info
mhegazy Oct 8, 2014
c439ae4
Add support for union properties in goto def
mhegazy Oct 8, 2014
95584e9
Addressing CR feedback
ahejlsberg Oct 8, 2014
fd5b808
Accepting new baselines
ahejlsberg Oct 8, 2014
3a17b02
Improved type argument inference with union types
ahejlsberg Oct 8, 2014
ea4cbbe
Merge branch 'master' into unionTypes
ahejlsberg Oct 8, 2014
5c661ba
Accepting new baselines after merge with master
ahejlsberg Oct 8, 2014
779db6e
Support find all refs on union properties
mhegazy Oct 9, 2014
2eb51ab
Use getRootSymbols for all union property needs
mhegazy Oct 9, 2014
dc43e83
Merge branch 'unionTypes' into unionTypesLS
mhegazy Oct 9, 2014
927f04f
Fix contextually typed object literal proeprties that are not propert…
mhegazy Oct 9, 2014
9f43ac0
respond to code review remarks
mhegazy Oct 10, 2014
bacb9d0
Test updates from union changes
danquirk Oct 10, 2014
8ce1760
Fixing merge conflicts
danquirk Oct 10, 2014
f5a9fee
ensure unionProperty symbols have declarations set at creation time
mhegazy Oct 10, 2014
483afea
Less aggressive subtype reduction in union types
ahejlsberg Oct 10, 2014
4e02b9f
Merge branch 'unionTypes' of https://github.com/Microsoft/TypeScript …
ahejlsberg Oct 10, 2014
c9a42c1
Accepting new baselines
ahejlsberg Oct 11, 2014
2ce627c
Handle union properties completions on apparant types
mhegazy Oct 11, 2014
4442b45
Add a temporary fix to quick info
mhegazy Oct 11, 2014
04e5309
Merge branch 'unionTypes' into unionTypesLS
mhegazy Oct 11, 2014
eee1602
Merge pull request #861 from Microsoft/unionTypesLS
mhegazy Oct 11, 2014
83d9aed
Correct contextual typing with union types
ahejlsberg Oct 13, 2014
a76a418
Accepting new baselines
ahejlsberg Oct 13, 2014
869ee41
Addressing CR feedback
ahejlsberg Oct 13, 2014
fc842b1
Merge branch 'master' into unionTypes
ahejlsberg Oct 13, 2014
4f4f59a
Merge changes from master in services.ts
mhegazy Oct 13, 2014
f5cd414
Merge branch 'master' into unionTypes
mhegazy Oct 13, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Addressing CR feedback
  • Loading branch information
ahejlsberg committed Oct 8, 2014
commit 95584e91041f705a59be8b1c70e940cc0ee508a8
107 changes: 66 additions & 41 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1157,7 +1157,7 @@ module ts {
}
}

function writeTypeList(types: Type[], union?: boolean) {
function writeTypeList(types: Type[], union: boolean) {
for (var i = 0; i < types.length; i++) {
if (i > 0) {
if (union) {
Expand All @@ -1166,6 +1166,8 @@ module ts {
writePunctuation(writer, union ? SyntaxKind.BarToken : SyntaxKind.CommaToken);
writeSpace(writer);
}
// Don't output function type literals in unions because '() => string | () => number' would be parsed
// as a function type that returns a union type. Instead output '{ (): string; } | { (): number; }'.
writeType(types[i], /*allowFunctionOrConstructorTypeLiteral*/ !union);
}
}
Expand All @@ -1181,14 +1183,14 @@ module ts {
else {
writeSymbol(type.target.symbol, writer, enclosingDeclaration, SymbolFlags.Type);
writePunctuation(writer, SyntaxKind.LessThanToken);
writeTypeList(type.typeArguments);
writeTypeList(type.typeArguments, /*union*/ false);
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}

function writeTupleType(type: TupleType) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeTypeList(type.elementTypes);
writeTypeList(type.elementTypes, /*union*/ false);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}

Expand Down Expand Up @@ -1744,7 +1746,7 @@ module ts {
function getTypeOfUnionProperty(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
var types = map(links.unionType.types, t => getTypeOfSymbol(getPropertyOfType(getApparentType(t), symbol.name) || undefinedSymbol));
var types = map(links.unionType.types, t => getTypeOfSymbol(getPropertyOfType(getApparentType(t), symbol.name)));
links.type = getUnionType(types);
}
return links.type;
Expand Down Expand Up @@ -2069,25 +2071,37 @@ module ts {
}

function signatureListsIdentical(s: Signature[], t: Signature[]): boolean {
if (s.length !== t.length) return false;
if (s.length !== t.length) {
return false;
}
for (var i = 0; i < s.length; i++) {
if (!compareSignatures(s[i], t[i], false, isTypeIdenticalTo)) return false;
if (!compareSignatures(s[i], t[i], /*compareReturnTypes*/ false, isTypeIdenticalTo)) {
return false;
}
}
return true;
}

// If the lists of call or construct signatures in the given types are all identical except for return types,
// and if none of the signatures are generic, return a list of signatures that has substitutes a union of the
// return types of the corresponding signatures in each resulting signature.
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
var signatureLists = map(types, t => getSignaturesOfType(t, kind));
var baseSignatures = signatureLists[0];
for (var i = 0; i < baseSignatures.length; i++) {
if (baseSignatures[i].typeParameters) return emptyArray;
var signatures = signatureLists[0];
for (var i = 0; i < signatures.length; i++) {
if (signatures[i].typeParameters) {
return emptyArray;
}
}
for (var i = 1; i < signatureLists.length; i++) {
if (!signatureListsIdentical(baseSignatures, signatureLists[i])) return emptyArray;
if (!signatureListsIdentical(signatures, signatureLists[i])) {
return emptyArray;
}
}
var result = map(baseSignatures, cloneSignature);
var result = map(signatures, cloneSignature);
for (var i = 0; i < result.length; i++) {
var s = result[i];
// Clear resolved return type we possibly got from cloneSignature
s.resolvedReturnType = undefined;
s.unionSignatures = map(signatureLists, signatures => signatures[i]);
Copy link
Contributor

Choose a reason for hiding this comment

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

comments on what sort of sturcture you're building up here would be very helpful. right now this line is hard to understand. You're mapping over all the signature lists, and for each over those, you're extracting out the i'ith signature?

}
Expand All @@ -2098,7 +2112,9 @@ module ts {
var indexTypes: Type[] = [];
for (var i = 0; i < types.length; i++) {
var indexType = getIndexTypeOfType(types[i], kind);
if (!indexType) return undefined;
if (!indexType) {
return undefined;
}
indexTypes.push(indexType);
}
return getUnionType(indexTypes);
Expand All @@ -2113,14 +2129,16 @@ module ts {
}
});
if (types.length <= 1) {
var res = types.length ? resolveObjectTypeMembers(types[0]) : emptyObjectType;
setObjectTypeMembers(type, res.members, res.callSignatures, res.constructSignatures, res.stringIndexType, res.numberIndexType);
var resolved = types.length ? resolveObjectTypeMembers(types[0]) : emptyObjectType;
setObjectTypeMembers(type, resolved.members, resolved.callSignatures, resolved.constructSignatures, resolved.stringIndexType, resolved.numberIndexType);
return;
}
var members: SymbolTable = {};
forEach(getPropertiesOfType(types[0]), prop => {
for (var i = 1; i < types.length; i++) {
if (!getPropertyOfType(types[i], prop.name)) return;
if (!getPropertyOfType(types[i], prop.name)) {
return;
}
}
var symbol = <TransientSymbol>createSymbol(SymbolFlags.UnionProperty | SymbolFlags.Transient, prop.name);
symbol.unionType = type;
Expand Down Expand Up @@ -2662,7 +2680,9 @@ module ts {
else {
var i = 0;
var id = type.id;
while (i < sortedSet.length && sortedSet[i].id < id) i++;
while (i < sortedSet.length && sortedSet[i].id < id) {
i++;
}
if (i === sortedSet.length || sortedSet[i].id !== id) {
sortedSet.splice(i, 0, type);
}
Expand All @@ -2677,7 +2697,9 @@ module ts {

function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
for (var i = 0, len = types.length; i < len; i++) {
if (candidate !== types[i] && isTypeSubtypeOf(candidate, types[i])) return true;
if (candidate !== types[i] && isTypeSubtypeOf(candidate, types[i])) {
return true;
}
}
return false;
}
Expand Down Expand Up @@ -3467,7 +3489,7 @@ module ts {
return false;
}
for (var i = 0, len = sourceSignatures.length; i < len; ++i) {
if (!compareSignatures(sourceSignatures[i], targetSignatures[i], /*returnTypes*/ true, isRelatedTo)) {
if (!compareSignatures(sourceSignatures[i], targetSignatures[i], /*compareReturnTypes*/ true, isRelatedTo)) {
return false;
}
}
Expand Down Expand Up @@ -3535,7 +3557,7 @@ module ts {
}
}

function compareSignatures(source: Signature, target: Signature, returnTypes: boolean, compareTypes: (s: Type, t: Type) => boolean): boolean {
function compareSignatures(source: Signature, target: Signature, compareReturnTypes: boolean, compareTypes: (s: Type, t: Type) => boolean): boolean {
if (source === target) {
return true;
}
Expand Down Expand Up @@ -3568,7 +3590,7 @@ module ts {
return false;
}
}
return !returnTypes || compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
return !compareReturnTypes || compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
}

function isSupertypeOfEach(candidate: Type, types: Type[]): boolean {
Expand Down Expand Up @@ -3876,20 +3898,14 @@ module ts {
if (type.flags & TypeFlags.Union) {
var types = (<UnionType>type).types;
if (forEach(types, t => t.flags & subtractMask)) {
var newTypes: Type[] = [];
forEach(types, t => {
if (!(t.flags & subtractMask)) {
newTypes.push(t);
}
});
return getUnionType(newTypes);
return getUnionType(filter(types, t => !(t.flags & subtractMask)));
}
}
return type;
}

// Check if a given variable is assigned within a given syntax node
function IsVariableAssignedWithin(symbol: Symbol, node: Node): boolean {
function isVariableAssignedWithin(symbol: Symbol, node: Node): boolean {
var links = getNodeLinks(node);
if (links.assignmentChecks) {
var cachedResult = links.assignmentChecks[symbol.id];
Expand Down Expand Up @@ -3981,29 +3997,32 @@ module ts {
case SyntaxKind.IfStatement:
// In a branch of an if statement, narrow based on controlling expression
if (child !== (<IfStatement>node).expression) {
narrowedType = narrowType(type, (<IfStatement>node).expression, child === (<IfStatement>node).thenStatement);
narrowedType = narrowType(type, (<IfStatement>node).expression, /*assumeTrue*/ child === (<IfStatement>node).thenStatement);
}
break;
case SyntaxKind.ConditionalExpression:
// In a branch of a conditional expression, narrow based on controlling condition
if (child !== (<ConditionalExpression>node).condition) {
narrowedType = narrowType(type, (<ConditionalExpression>node).condition, child === (<ConditionalExpression>node).whenTrue);
narrowedType = narrowType(type, (<ConditionalExpression>node).condition, /*assumeTrue*/ child === (<ConditionalExpression>node).whenTrue);
}
break;
case SyntaxKind.BinaryExpression:
// In the right operand of an && or ||, narrow based on left operand
if (child === (<BinaryExpression>node).right) {
if ((<BinaryExpression>node).operator === SyntaxKind.AmpersandAmpersandToken) {
narrowedType = narrowType(type, (<BinaryExpression>node).left, true);
narrowedType = narrowType(type, (<BinaryExpression>node).left, /*assumeTrue*/ true);
}
else if ((<BinaryExpression>node).operator === SyntaxKind.BarBarToken) {
narrowedType = narrowType(type, (<BinaryExpression>node).left, false);
narrowedType = narrowType(type, (<BinaryExpression>node).left, /*assumeTrue*/ false);
}
}
break;
}
// Only use narrowed type if construct contains no assignments to variable
if (narrowedType !== type && !IsVariableAssignedWithin(symbol, node)) {
if (narrowedType !== type) {
if (isVariableAssignedWithin(symbol, node)) {
break;
}
type = narrowedType;
}
}
Expand Down Expand Up @@ -4039,30 +4058,36 @@ module ts {
function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
if (assumeTrue) {
// The assumed result is true, therefore we narrow assuming each operand to be true.
return narrowType(narrowType(type, expr.left, true), expr.right, true);
return narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ true);
}
else {
// The assumed result is true. This means either the first operand was false, or the first operand was true
// The assumed result is false. This means either the first operand was false, or the first operand was true
// and the second operand was false. We narrow with those assumptions and union the two resulting types.
return getUnionType([narrowType(type, expr.left, false), narrowType(narrowType(type, expr.left, true), expr.right, false)]);
return getUnionType([
narrowType(type, expr.left, /*assumeTrue*/ false),
narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ false)
]);
}
}

function narrowTypeByOr(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
if (assumeTrue) {
// The assumed result is true. This means either the first operand was true, or the first operand was false
// and the second operand was true. We narrow with those assumptions and union the two resulting types.
return getUnionType([narrowType(type, expr.left, true), narrowType(narrowType(type, expr.left, false), expr.right, true)]);
return getUnionType([
narrowType(type, expr.left, /*assumeTrue*/ true),
narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ true)
]);
}
else {
// The assumed result is false, therefore we narrow assuming each operand to be false.
return narrowType(narrowType(type, expr.left, false), expr.right, false);
return narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ false);
}
}

function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
// Check that we have variable symbol on the left
if (expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>expr.left) !== symbol) {
// Check that assumed result is true and we have variable symbol on the left
if (!assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>expr.left) !== symbol) {
return type;
}
// Check that right operand is a function type with a prototype property
Expand Down Expand Up @@ -7198,7 +7223,7 @@ module ts {
return checkArrayType(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return checkTupleType(<TupleTypeNode>node);
case SyntaxKind.TupleType:
case SyntaxKind.UnionType:
return checkUnionType(<UnionTypeNode>node);
case SyntaxKind.FunctionDeclaration:
return checkFunctionDeclaration(<FunctionDeclaration>node);
Expand Down