Skip to content

Commit 71c6433

Browse files
committed
Merge pull request microsoft#3622 from Microsoft/intersectionTypes
Intersection types
2 parents f126767 + fc1888e commit 71c6433

36 files changed

+1543
-189
lines changed

Diff for: src/compiler/checker.ts

+266-150
Large diffs are not rendered by default.

Diff for: src/compiler/declarationEmitter.ts

+6
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ namespace ts {
340340
return emitTupleType(<TupleTypeNode>type);
341341
case SyntaxKind.UnionType:
342342
return emitUnionType(<UnionTypeNode>type);
343+
case SyntaxKind.IntersectionType:
344+
return emitIntersectionType(<IntersectionTypeNode>type);
343345
case SyntaxKind.ParenthesizedType:
344346
return emitParenType(<ParenthesizedTypeNode>type);
345347
case SyntaxKind.FunctionType:
@@ -416,6 +418,10 @@ namespace ts {
416418
emitSeparatedList(type.types, " | ", emitType);
417419
}
418420

421+
function emitIntersectionType(type: IntersectionTypeNode) {
422+
emitSeparatedList(type.types, " & ", emitType);
423+
}
424+
419425
function emitParenType(type: ParenthesizedTypeNode) {
420426
write("(");
421427
emitType(type.type);

Diff for: src/compiler/parser.ts

+16-7
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ namespace ts {
115115
case SyntaxKind.TupleType:
116116
return visitNodes(cbNodes, (<TupleTypeNode>node).elementTypes);
117117
case SyntaxKind.UnionType:
118-
return visitNodes(cbNodes, (<UnionTypeNode>node).types);
118+
case SyntaxKind.IntersectionType:
119+
return visitNodes(cbNodes, (<UnionOrIntersectionTypeNode>node).types);
119120
case SyntaxKind.ParenthesizedType:
120121
return visitNode(cbNode, (<ParenthesizedTypeNode>node).type);
121122
case SyntaxKind.ObjectBindingPattern:
@@ -2401,22 +2402,30 @@ namespace ts {
24012402
return type;
24022403
}
24032404

2404-
function parseUnionTypeOrHigher(): TypeNode {
2405-
let type = parseArrayTypeOrHigher();
2406-
if (token === SyntaxKind.BarToken) {
2405+
function parseUnionOrIntersectionType(kind: SyntaxKind, parseConstituentType: () => TypeNode, operator: SyntaxKind): TypeNode {
2406+
let type = parseConstituentType();
2407+
if (token === operator) {
24072408
let types = <NodeArray<TypeNode>>[type];
24082409
types.pos = type.pos;
2409-
while (parseOptional(SyntaxKind.BarToken)) {
2410-
types.push(parseArrayTypeOrHigher());
2410+
while (parseOptional(operator)) {
2411+
types.push(parseConstituentType());
24112412
}
24122413
types.end = getNodeEnd();
2413-
let node = <UnionTypeNode>createNode(SyntaxKind.UnionType, type.pos);
2414+
let node = <UnionOrIntersectionTypeNode>createNode(kind, type.pos);
24142415
node.types = types;
24152416
type = finishNode(node);
24162417
}
24172418
return type;
24182419
}
24192420

2421+
function parseIntersectionTypeOrHigher(): TypeNode {
2422+
return parseUnionOrIntersectionType(SyntaxKind.IntersectionType, parseArrayTypeOrHigher, SyntaxKind.AmpersandToken);
2423+
}
2424+
2425+
function parseUnionTypeOrHigher(): TypeNode {
2426+
return parseUnionOrIntersectionType(SyntaxKind.UnionType, parseIntersectionTypeOrHigher, SyntaxKind.BarToken);
2427+
}
2428+
24202429
function isStartOfFunctionType(): boolean {
24212430
if (token === SyntaxKind.LessThanToken) {
24222431
return true;

Diff for: src/compiler/types.ts

+26-14
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ namespace ts {
191191
ArrayType,
192192
TupleType,
193193
UnionType,
194+
IntersectionType,
194195
ParenthesizedType,
195196
// Binding patterns
196197
ObjectBindingPattern,
@@ -667,10 +668,14 @@ namespace ts {
667668
elementTypes: NodeArray<TypeNode>;
668669
}
669670

670-
export interface UnionTypeNode extends TypeNode {
671+
export interface UnionOrIntersectionTypeNode extends TypeNode {
671672
types: NodeArray<TypeNode>;
672673
}
673674

675+
export interface UnionTypeNode extends UnionOrIntersectionTypeNode { }
676+
677+
export interface IntersectionTypeNode extends UnionOrIntersectionTypeNode { }
678+
674679
export interface ParenthesizedTypeNode extends TypeNode {
675680
type: TypeNode;
676681
}
@@ -1573,7 +1578,7 @@ namespace ts {
15731578
Merged = 0x02000000, // Merged symbol (created during program binding)
15741579
Transient = 0x04000000, // Transient symbol (created during type check)
15751580
Prototype = 0x08000000, // Prototype property (no source representation)
1576-
UnionProperty = 0x10000000, // Property in union type
1581+
SyntheticProperty = 0x10000000, // Property in union or intersection type
15771582
Optional = 0x20000000, // Optional property
15781583
ExportStar = 0x40000000, // Export * declaration
15791584

@@ -1652,7 +1657,7 @@ namespace ts {
16521657
instantiations?: Map<Type>; // Instantiations of generic type alias (undefined if non-generic)
16531658
mapper?: TypeMapper; // Type mapper for instantiation alias
16541659
referenced?: boolean; // True if alias symbol has been referenced as a value
1655-
unionType?: UnionType; // Containing union type for union property
1660+
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
16561661
resolvedExports?: SymbolTable; // Resolved exports of module
16571662
exportsChecked?: boolean; // True if exports of external module have been checked
16581663
isNestedRedeclaration?: boolean; // True if symbol is block scoped redeclaration
@@ -1721,17 +1726,18 @@ namespace ts {
17211726
Interface = 0x00000800, // Interface
17221727
Reference = 0x00001000, // Generic type reference
17231728
Tuple = 0x00002000, // Tuple
1724-
Union = 0x00004000, // Union
1725-
Anonymous = 0x00008000, // Anonymous
1726-
Instantiated = 0x00010000, // Instantiated anonymous type
1729+
Union = 0x00004000, // Union (T | U)
1730+
Intersection = 0x00008000, // Intersection (T & U)
1731+
Anonymous = 0x00010000, // Anonymous
1732+
Instantiated = 0x00020000, // Instantiated anonymous type
17271733
/* @internal */
1728-
FromSignature = 0x00020000, // Created for signature assignment check
1729-
ObjectLiteral = 0x00040000, // Originates in an object literal
1734+
FromSignature = 0x00040000, // Created for signature assignment check
1735+
ObjectLiteral = 0x00080000, // Originates in an object literal
17301736
/* @internal */
1731-
ContainsUndefinedOrNull = 0x00080000, // Type is or contains Undefined or Null type
1737+
ContainsUndefinedOrNull = 0x00100000, // Type is or contains Undefined or Null type
17321738
/* @internal */
1733-
ContainsObjectLiteral = 0x00100000, // Type is or contains object literal type
1734-
ESSymbol = 0x00200000, // Type of symbol primitive introduced in ES6
1739+
ContainsObjectLiteral = 0x00200000, // Type is or contains object literal type
1740+
ESSymbol = 0x00400000, // Type of symbol primitive introduced in ES6
17351741

17361742
/* @internal */
17371743
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
@@ -1740,6 +1746,8 @@ namespace ts {
17401746
StringLike = String | StringLiteral,
17411747
NumberLike = Number | Enum,
17421748
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
1749+
UnionOrIntersection = Union | Intersection,
1750+
StructuredType = ObjectType | Union | Intersection,
17431751
/* @internal */
17441752
RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral
17451753
}
@@ -1799,17 +1807,21 @@ namespace ts {
17991807
baseArrayType: TypeReference; // Array<T> where T is best common type of element types
18001808
}
18011809

1802-
export interface UnionType extends Type {
1810+
export interface UnionOrIntersectionType extends Type {
18031811
types: Type[]; // Constituent types
18041812
/* @internal */
18051813
reducedType: Type; // Reduced union type (all subtypes removed)
18061814
/* @internal */
18071815
resolvedProperties: SymbolTable; // Cache of resolved properties
18081816
}
18091817

1818+
export interface UnionType extends UnionOrIntersectionType { }
1819+
1820+
export interface IntersectionType extends UnionOrIntersectionType { }
1821+
18101822
/* @internal */
1811-
// Resolved object or union type
1812-
export interface ResolvedType extends ObjectType, UnionType {
1823+
// Resolved object, union, or intersection type
1824+
export interface ResolvedType extends ObjectType, UnionOrIntersectionType {
18131825
members: SymbolTable; // Properties by name
18141826
properties: Symbol[]; // Properties
18151827
callSignatures: Signature[]; // Call signatures of type

Diff for: src/services/services.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3689,7 +3689,7 @@ namespace ts {
36893689
if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement;
36903690

36913691
if (flags & SymbolFlags.Property) {
3692-
if (flags & SymbolFlags.UnionProperty) {
3692+
if (flags & SymbolFlags.SyntheticProperty) {
36933693
// If union property is result of union of non method (property/accessors/variables), it is labeled as property
36943694
let unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => {
36953695
let rootSymbolFlags = rootSymbol.getFlags();
@@ -5159,7 +5159,7 @@ namespace ts {
51595159

51605160
// if this symbol is visible from its parent container, e.g. exported, then bail out
51615161
// if symbol correspond to the union property - bail out
5162-
if (symbol.parent || (symbol.flags & SymbolFlags.UnionProperty)) {
5162+
if (symbol.parent || (symbol.flags & SymbolFlags.SyntheticProperty)) {
51635163
return undefined;
51645164
}
51655165

Diff for: tests/baselines/reference/APISample_linter.js

+10-10
Original file line numberDiff line numberDiff line change
@@ -75,26 +75,26 @@ function delint(sourceFile) {
7575
delintNode(sourceFile);
7676
function delintNode(node) {
7777
switch (node.kind) {
78-
case 195 /* ForStatement */:
79-
case 196 /* ForInStatement */:
80-
case 194 /* WhileStatement */:
81-
case 193 /* DoStatement */:
82-
if (node.statement.kind !== 188 /* Block */) {
78+
case 196 /* ForStatement */:
79+
case 197 /* ForInStatement */:
80+
case 195 /* WhileStatement */:
81+
case 194 /* DoStatement */:
82+
if (node.statement.kind !== 189 /* Block */) {
8383
report(node, "A looping statement's contents should be wrapped in a block body.");
8484
}
8585
break;
86-
case 192 /* IfStatement */:
86+
case 193 /* IfStatement */:
8787
var ifStatement = node;
88-
if (ifStatement.thenStatement.kind !== 188 /* Block */) {
88+
if (ifStatement.thenStatement.kind !== 189 /* Block */) {
8989
report(ifStatement.thenStatement, "An if statement's contents should be wrapped in a block body.");
9090
}
9191
if (ifStatement.elseStatement &&
92-
ifStatement.elseStatement.kind !== 188 /* Block */ &&
93-
ifStatement.elseStatement.kind !== 192 /* IfStatement */) {
92+
ifStatement.elseStatement.kind !== 189 /* Block */ &&
93+
ifStatement.elseStatement.kind !== 193 /* IfStatement */) {
9494
report(ifStatement.elseStatement, "An else statement's contents should be wrapped in a block body.");
9595
}
9696
break;
97-
case 177 /* BinaryExpression */:
97+
case 178 /* BinaryExpression */:
9898
var op = node.operatorToken.kind;
9999
if (op === 29 /* EqualsEqualsToken */ || op == 30 /* ExclamationEqualsToken */) {
100100
report(node, "Use '===' and '!=='.");
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//// [contextualIntersectionType.ts]
2+
var x: { a: (s: string) => string } & { b: (n: number) => number };
3+
x = {
4+
a: s => s,
5+
b: n => n
6+
};
7+
8+
9+
//// [contextualIntersectionType.js]
10+
var x;
11+
x = {
12+
a: function (s) { return s; },
13+
b: function (n) { return n; }
14+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/conformance/types/intersection/contextualIntersectionType.ts ===
2+
var x: { a: (s: string) => string } & { b: (n: number) => number };
3+
>x : Symbol(x, Decl(contextualIntersectionType.ts, 0, 3))
4+
>a : Symbol(a, Decl(contextualIntersectionType.ts, 0, 8))
5+
>s : Symbol(s, Decl(contextualIntersectionType.ts, 0, 13))
6+
>b : Symbol(b, Decl(contextualIntersectionType.ts, 0, 39))
7+
>n : Symbol(n, Decl(contextualIntersectionType.ts, 0, 44))
8+
9+
x = {
10+
>x : Symbol(x, Decl(contextualIntersectionType.ts, 0, 3))
11+
12+
a: s => s,
13+
>a : Symbol(a, Decl(contextualIntersectionType.ts, 1, 5))
14+
>s : Symbol(s, Decl(contextualIntersectionType.ts, 2, 6))
15+
>s : Symbol(s, Decl(contextualIntersectionType.ts, 2, 6))
16+
17+
b: n => n
18+
>b : Symbol(b, Decl(contextualIntersectionType.ts, 2, 14))
19+
>n : Symbol(n, Decl(contextualIntersectionType.ts, 3, 6))
20+
>n : Symbol(n, Decl(contextualIntersectionType.ts, 3, 6))
21+
22+
};
23+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/conformance/types/intersection/contextualIntersectionType.ts ===
2+
var x: { a: (s: string) => string } & { b: (n: number) => number };
3+
>x : { a: (s: string) => string; } & { b: (n: number) => number; }
4+
>a : (s: string) => string
5+
>s : string
6+
>b : (n: number) => number
7+
>n : number
8+
9+
x = {
10+
>x = { a: s => s, b: n => n} : { a: (s: string) => string; b: (n: number) => number; }
11+
>x : { a: (s: string) => string; } & { b: (n: number) => number; }
12+
>{ a: s => s, b: n => n} : { a: (s: string) => string; b: (n: number) => number; }
13+
14+
a: s => s,
15+
>a : (s: string) => string
16+
>s => s : (s: string) => string
17+
>s : string
18+
>s : string
19+
20+
b: n => n
21+
>b : (n: number) => number
22+
>n => n : (n: number) => number
23+
>n : number
24+
>n : number
25+
26+
};
27+

0 commit comments

Comments
 (0)