Skip to content

Commit

Permalink
Initial implementation of Union Types
Browse files Browse the repository at this point in the history
  • Loading branch information
ahejlsberg committed Oct 4, 2014
1 parent 438aa89 commit e836fe1
Show file tree
Hide file tree
Showing 6 changed files with 407 additions and 209 deletions.
543 changes: 360 additions & 183 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ module ts {
return array1.concat(array2);
}

export function uniqueElements<T>(array: T[]): T[] {
export function deduplicate<T>(array: T[]): T[] {
if (array) {
var result: T[] = [];
for (var i = 0, len = array.length; i < len; i++) {
Expand Down
2 changes: 0 additions & 2 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,6 @@ module ts {
The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2363, category: DiagnosticCategory.Error, key: "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
Invalid_left_hand_side_of_assignment_expression: { code: 2364, category: DiagnosticCategory.Error, key: "Invalid left-hand side of assignment expression." },
Operator_0_cannot_be_applied_to_types_1_and_2: { code: 2365, category: DiagnosticCategory.Error, key: "Operator '{0}' cannot be applied to types '{1}' and '{2}'." },
No_best_common_type_exists_between_0_1_and_2: { code: 2366, category: DiagnosticCategory.Error, key: "No best common type exists between '{0}', '{1}', and '{2}'." },
No_best_common_type_exists_between_0_and_1: { code: 2367, category: DiagnosticCategory.Error, key: "No best common type exists between '{0}' and '{1}'." },
Type_parameter_name_cannot_be_0: { code: 2368, category: DiagnosticCategory.Error, key: "Type parameter name cannot be '{0}'" },
A_parameter_property_is_only_allowed_in_a_constructor_implementation: { code: 2369, category: DiagnosticCategory.Error, key: "A parameter property is only allowed in a constructor implementation." },
A_rest_parameter_must_be_of_an_array_type: { code: 2370, category: DiagnosticCategory.Error, key: "A rest parameter must be of an array type." },
Expand Down
8 changes: 0 additions & 8 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -708,14 +708,6 @@
"category": "Error",
"code": 2365
},
"No best common type exists between '{0}', '{1}', and '{2}'.": {
"category": "Error",
"code": 2366
},
"No best common type exists between '{0}' and '{1}'.": {
"category": "Error",
"code": 2367
},
"Type parameter name cannot be '{0}'": {
"category": "Error",
"code": 2368
Expand Down
22 changes: 20 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ module ts {
return child((<ArrayTypeNode>node).elementType);
case SyntaxKind.TupleType:
return children((<TupleTypeNode>node).elementTypes);
case SyntaxKind.UnionType:
return children((<UnionTypeNode>node).types);
case SyntaxKind.ArrayLiteral:
return children((<ArrayLiteral>node).elements);
case SyntaxKind.ObjectLiteral:
Expand Down Expand Up @@ -1728,9 +1730,9 @@ module ts {
}
}

function parseType(): TypeNode {
function parseNonUnionType(): TypeNode {
var type = parseNonArrayType();
while (type && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
parseExpected(SyntaxKind.CloseBracketToken);
var node = <ArrayTypeNode>createNode(SyntaxKind.ArrayType, type.pos);
node.elementType = type;
Expand All @@ -1739,6 +1741,22 @@ module ts {
return type;
}

function parseType(): TypeNode {
var type = parseNonUnionType();
if (token === SyntaxKind.BarToken) {
var types = <NodeArray<TypeNode>>[type];
types.pos = type.pos;
while (parseOptional(SyntaxKind.BarToken)) {
types.push(parseNonUnionType());
}
types.end = getNodeEnd();
var node = <UnionTypeNode>createNode(SyntaxKind.UnionType, type.pos);
node.types = types;
type = finishNode(node);
}
return type;
}

function parseTypeAnnotation(): TypeNode {
return parseOptional(SyntaxKind.ColonToken) ? parseType() : undefined;
}
Expand Down
39 changes: 26 additions & 13 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ module ts {
TypeLiteral,
ArrayType,
TupleType,
UnionType,
// Expression
ArrayLiteral,
ObjectLiteral,
Expand Down Expand Up @@ -224,7 +225,7 @@ module ts {
FirstFutureReservedWord = ImplementsKeyword,
LastFutureReservedWord = YieldKeyword,
FirstTypeNode = TypeReference,
LastTypeNode = TupleType,
LastTypeNode = UnionType,
FirstPunctuation = OpenBraceToken,
LastPunctuation = CaretEqualsToken,
FirstToken = EndOfFileToken,
Expand Down Expand Up @@ -337,6 +338,10 @@ module ts {
elementTypes: NodeArray<TypeNode>;
}

export interface UnionTypeNode extends TypeNode {
types: NodeArray<TypeNode>;
}

export interface StringLiteralTypeNode extends TypeNode {
text: string;
}
Expand Down Expand Up @@ -728,19 +733,20 @@ module ts {
ConstructSignature = 0x00010000, // Construct signature
IndexSignature = 0x00020000, // Index signature
TypeParameter = 0x00040000, // Type parameter
UnionProperty = 0x00080000, // Property in union type

// Export markers (see comment in declareModuleMember in binder)
ExportValue = 0x00080000, // Exported value marker
ExportType = 0x00100000, // Exported type marker
ExportNamespace = 0x00200000, // Exported namespace marker
ExportValue = 0x00100000, // Exported value marker
ExportType = 0x00200000, // Exported type marker
ExportNamespace = 0x00400000, // Exported namespace marker

Import = 0x00400000, // Import
Instantiated = 0x00800000, // Instantiated symbol
Merged = 0x01000000, // Merged symbol (created during program binding)
Transient = 0x02000000, // Transient symbol (created during type check)
Prototype = 0x04000000, // Symbol for the prototype property (without source code representation)
Import = 0x00800000, // Import
Instantiated = 0x01000000, // Instantiated symbol
Merged = 0x02000000, // Merged symbol (created during program binding)
Transient = 0x04000000, // Transient symbol (created during type check)
Prototype = 0x08000000, // Prototype property (no source representation)

Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor,
Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor | UnionProperty,
Type = Class | Interface | Enum | TypeLiteral | ObjectLiteral | TypeParameter,
Namespace = ValueModule | NamespaceModule,
Module = ValueModule | NamespaceModule,
Expand Down Expand Up @@ -798,6 +804,7 @@ module ts {
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
exportAssignSymbol?: Symbol; // Symbol exported from external module
unionType?: UnionType; // Containing union type for union property
}

export interface TransientSymbol extends Symbol, SymbolLinks { }
Expand Down Expand Up @@ -845,13 +852,14 @@ module ts {
Interface = 0x00000800, // Interface
Reference = 0x00001000, // Generic type reference
Tuple = 0x00002000, // Tuple
Anonymous = 0x00004000, // Anonymous
FromSignature = 0x00008000, // Created for signature assignment check
Union = 0x00004000, // Union
Anonymous = 0x00008000, // Anonymous
FromSignature = 0x00010000, // Created for signature assignment check

Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null,
StringLike = String | StringLiteral,
NumberLike = Number | Enum,
ObjectType = Class | Interface | Reference | Tuple | Anonymous
ObjectType = Class | Interface | Reference | Tuple | Union | Anonymous
}

// Properties common to all types
Expand Down Expand Up @@ -909,6 +917,10 @@ module ts {
baseArrayType: TypeReference; // Array<T> where T is best common type of element types
}

export interface UnionType extends ObjectType {
types: Type[]; // Constituent types
}

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Oct 6, 2014

Contributor

I find it slightly unnatural for Union types to extend Object types. Is the purpose of this so we do not have to create two objects for each union type? I think this is a reasonable design, but just want to make sure I understand it.

This comment has been minimized.

Copy link
@ahejlsberg

ahejlsberg Oct 7, 2014

Author Member

Not sure I understand the question. We treat a union type as an object type because we want to defer member resolution and other such capabilities that we only have in object types.


// Resolved object type
export interface ResolvedObjectType extends ObjectType {
members: SymbolTable; // Properties by name
Expand Down Expand Up @@ -941,6 +953,7 @@ module ts {
hasStringLiterals: boolean; // True if specialized
target?: Signature; // Instantiation target
mapper?: TypeMapper; // Instantiation mapper
unionSignatures?: Signature[]; // Underlying signatures of a union signature
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
}
Expand Down

0 comments on commit e836fe1

Please sign in to comment.