Skip to content

Commit fe70bf2

Browse files
rklfssblakeembrey
authored andcommitted
Support for intersection types (TypeStrong#482)
1 parent 2ba0f0f commit fe70bf2

17 files changed

+497
-32
lines changed

src/lib/converter/plugins/TypePlugin.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Reflection, ReflectionKind, Decorator, DeclarationReflection, DeclarationHierarchy} from '../../models/reflections/index';
2-
import {Type, ReferenceType, TupleType, UnionType} from '../../models/types/index';
2+
import {Type, ReferenceType, TupleType, UnionType, IntersectionType} from '../../models/types/index';
33
import {Component, ConverterComponent} from '../components';
44
import {Converter} from '../converter';
55
import {Context} from '../context';
@@ -108,10 +108,10 @@ export class TypePlugin extends ConverterComponent {
108108
for (let index = 0, count = tupleType.elements.length; index < count; index++) {
109109
resolveType(reflection, tupleType.elements[index]);
110110
}
111-
} else if (type instanceof UnionType) {
112-
const unionType: UnionType = <UnionType> type;
113-
for (let index = 0, count = unionType.types.length; index < count; index++) {
114-
resolveType(reflection, unionType.types[index]);
111+
} else if (type instanceof UnionType || type instanceof IntersectionType) {
112+
const unionOrIntersectionType: UnionType | IntersectionType = <UnionType | IntersectionType> type;
113+
for (let index = 0, count = unionOrIntersectionType.types.length; index < count; index++) {
114+
resolveType(reflection, unionOrIntersectionType.types[index]);
115115
}
116116
}
117117
}

src/lib/converter/types/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ export {ReferenceConverter} from './reference';
99
export {ThisConverter} from './this';
1010
export {TupleConverter} from './tuple';
1111
export {TypeParameterConverter} from './type-parameter';
12-
export {UnionConverter} from './union';
12+
export {UnionOrIntersectionConverter} from './union-or-intersection';
1313
export {UnknownConverter} from './unknown';
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,47 @@
11
import * as ts from 'typescript';
22

3-
import {Type, UnionType} from '../../models/types/index';
3+
import {Type, UnionType, IntersectionType} from '../../models/types/index';
44
import {Component, ConverterTypeComponent, TypeConverter} from '../components';
55
import {Context} from '../context';
66

7-
@Component({name: 'type:union'})
8-
export class UnionConverter extends ConverterTypeComponent implements TypeConverter<ts.UnionType, ts.UnionTypeNode> {
7+
@Component({name: 'type:union-or-intersection'})
8+
export class UnionOrIntersectionConverter extends ConverterTypeComponent implements TypeConverter<ts.UnionOrIntersectionType, ts.UnionOrIntersectionTypeNode> {
99
/**
1010
* Test whether this converter can handle the given TypeScript node.
1111
*/
12-
supportsNode(context: Context, node: ts.UnionTypeNode): boolean {
13-
return node.kind === ts.SyntaxKind.UnionType;
12+
supportsNode(context: Context, node: ts.UnionOrIntersectionTypeNode): boolean {
13+
return node.kind === ts.SyntaxKind.UnionType || node.kind === ts.SyntaxKind.IntersectionType;
1414
}
1515

1616
/**
1717
* Test whether this converter can handle the given TypeScript type.
1818
*/
19-
supportsType(context: Context, type: ts.UnionType): boolean {
20-
return !!(type.flags & ts.TypeFlags.Union);
19+
supportsType(context: Context, type: ts.UnionOrIntersectionType): boolean {
20+
return !!(type.flags & ts.TypeFlags.UnionOrIntersection);
2121
}
2222

2323
/**
2424
* Convert the given union type node to its type reflection.
2525
*
26-
* This is a node based converter, see [[convertUnionType]] for the type equivalent.
26+
* This is a node based converter, see [[convertType]] for the type equivalent.
2727
*
2828
* ```
2929
* let someValue: string|number;
3030
* ```
3131
*
3232
* @param context The context object describing the current state the converter is in.
33-
* @param node The union type node that should be converted.
33+
* @param node The union or intersection type node that should be converted.
3434
* @returns The type reflection representing the given union type node.
3535
*/
36-
convertNode(context: Context, node: ts.UnionTypeNode): UnionType {
36+
convertNode(context: Context, node: ts.UnionOrIntersectionTypeNode): UnionType | IntersectionType {
3737
let types: Type[] = [];
3838
if (node.types) {
3939
types = node.types.map((n) => this.owner.convertType(context, n));
4040
} else {
4141
types = [];
4242
}
4343

44-
return new UnionType(types);
44+
return node.kind === ts.SyntaxKind.IntersectionType ? new IntersectionType(types) : new UnionType(types);
4545
}
4646

4747
/**
@@ -57,14 +57,14 @@ export class UnionConverter extends ConverterTypeComponent implements TypeConver
5757
* @param type The union type that should be converted.
5858
* @returns The type reflection representing the given union type.
5959
*/
60-
convertType(context: Context, type: ts.UnionType): UnionType {
60+
convertType(context: Context, type: ts.UnionOrIntersectionType): UnionType | IntersectionType {
6161
let types: Type[];
6262
if (type && type.types) {
6363
types = type.types.map((t) => this.owner.convertType(context, null, t));
6464
} else {
6565
types = [];
6666
}
6767

68-
return new UnionType(types);
68+
return !!(type.flags & ts.TypeFlags.Intersection) ? new IntersectionType(types) : new UnionType(types);
6969
}
7070
}

src/lib/models/types/abstract.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ export abstract class Type {
99
*/
1010
isArray = false;
1111

12+
/**
13+
* The type name identifier.
14+
*/
15+
readonly type: string = 'void';
16+
1217
/**
1318
* Clone this type.
1419
*
@@ -31,7 +36,7 @@ export abstract class Type {
3136
*/
3237
toObject(): any {
3338
let result: any = {};
34-
result.type = 'void';
39+
result.type = this.type;
3540

3641
if (this.isArray) {
3742
result.isArray = this.isArray;

src/lib/models/types/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export {Type} from './abstract';
22
export {IntrinsicType} from './intrinsic';
3+
export {IntersectionType} from './intersection';
34
export {ReferenceType} from './reference';
45
export {ReflectionType} from './reflection';
56
export {StringLiteralType} from './string-literal';

src/lib/models/types/intersection.ts

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {Type} from './abstract';
2+
3+
/**
4+
* Represents an intersection type.
5+
*
6+
* ~~~
7+
* let value: A & B;
8+
* ~~~
9+
*/
10+
export class IntersectionType extends Type {
11+
/**
12+
* The types this union consists of.
13+
*/
14+
types: Type[];
15+
16+
/**
17+
* The type name identifier.
18+
*/
19+
readonly type: string = 'intersection';
20+
21+
/**
22+
* Create a new TupleType instance.
23+
*
24+
* @param types The types this union consists of.
25+
*/
26+
constructor(types: Type[]) {
27+
super();
28+
this.types = types;
29+
}
30+
31+
/**
32+
* Clone this type.
33+
*
34+
* @return A clone of this type.
35+
*/
36+
clone(): Type {
37+
const clone = new IntersectionType(this.types);
38+
clone.isArray = this.isArray;
39+
return clone;
40+
}
41+
42+
/**
43+
* Test whether this type equals the given type.
44+
*
45+
* @param type The type that should be checked for equality.
46+
* @returns TRUE if the given type equals this type, FALSE otherwise.
47+
*/
48+
equals(type: IntersectionType): boolean {
49+
if (!(type instanceof IntersectionType)) {
50+
return false;
51+
}
52+
if (type.isArray !== this.isArray) {
53+
return false;
54+
}
55+
return Type.isTypeListSimiliar(type.types, this.types);
56+
}
57+
58+
/**
59+
* Return a raw object representation of this type.
60+
*/
61+
toObject(): any {
62+
const result: any = super.toObject();
63+
64+
if (this.types && this.types.length) {
65+
result.types = this.types.map((e) => e.toObject());
66+
}
67+
68+
return result;
69+
}
70+
71+
/**
72+
* Return a string representation of this type.
73+
*/
74+
toString() {
75+
const names: string[] = [];
76+
this.types.forEach((element) => {
77+
names.push(element.toString());
78+
});
79+
80+
return names.join(' & ');
81+
}
82+
}

src/lib/models/types/intrinsic.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export class IntrinsicType extends Type {
1313
*/
1414
name: string;
1515

16+
/**
17+
* The type name identifier.
18+
*/
19+
readonly type: string = 'instrinct'; // TODO: Is there any change to correct this typo?
20+
1621
/**
1722
* Create a new instance of IntrinsicType.
1823
*
@@ -51,7 +56,6 @@ export class IntrinsicType extends Type {
5156
*/
5257
toObject(): any {
5358
const result: any = super.toObject();
54-
result.type = 'instrinct';
5559
result.name = this.name;
5660
return result;
5761
}

src/lib/models/types/reference.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {Type} from './abstract';
99
* ~~~
1010
*/
1111
export class ReferenceType extends Type {
12+
/**
13+
* The type name identifier.
14+
*/
15+
readonly type: string = 'reference';
16+
1217
/**
1318
* The name of the referenced type.
1419
*
@@ -90,7 +95,6 @@ export class ReferenceType extends Type {
9095
*/
9196
toObject(): any {
9297
const result: any = super.toObject();
93-
result.type = 'reference';
9498
result.name = this.name;
9599

96100
if (this.reflection) {

src/lib/models/types/reflection.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ export class ReflectionType extends Type {
1414
*/
1515
declaration: DeclarationReflection;
1616

17+
/**
18+
* The type name identifier.
19+
*/
20+
readonly type: string = 'reflection';
21+
1722
/**
1823
* Create a new instance of ReflectionType.
1924
*
@@ -50,7 +55,6 @@ export class ReflectionType extends Type {
5055
*/
5156
toObject(): any {
5257
const result: any = super.toObject();
53-
result.type = 'reflection';
5458

5559
if (this.declaration) {
5660
result.declaration = this.declaration.toObject();

src/lib/models/types/string-literal.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export class StringLiteralType extends Type {
1313
*/
1414
value: string;
1515

16+
/**
17+
* The type name identifier.
18+
*/
19+
readonly type: string = 'stringLiteral';
20+
1621
/**
1722
* Create a new instance of StringLiteralType.
1823
*
@@ -51,7 +56,6 @@ export class StringLiteralType extends Type {
5156
*/
5257
toObject(): any {
5358
const result: any = super.toObject();
54-
result.type = 'stringLiteral';
5559
result.value = this.value;
5660
return result;
5761
}

src/lib/models/types/tuple.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export class TupleType extends Type {
1313
*/
1414
elements: Type[];
1515

16+
/**
17+
* The type name identifier.
18+
*/
19+
readonly type: string = 'tuple';
20+
1621
/**
1722
* Create a new TupleType instance.
1823
*
@@ -55,7 +60,6 @@ export class TupleType extends Type {
5560
*/
5661
toObject(): any {
5762
const result: any = super.toObject();
58-
result.type = 'tuple';
5963

6064
if (this.elements && this.elements.length) {
6165
result.elements = this.elements.map((e) => e.toObject());

src/lib/models/types/type-parameter.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ export class TypeParameterType extends Type {
1515

1616
constraint: Type;
1717

18+
/**
19+
* The type name identifier.
20+
*/
21+
readonly type: string = 'typeParameter';
22+
1823
/**
1924
* Clone this type.
2025
*
@@ -57,7 +62,6 @@ export class TypeParameterType extends Type {
5762
*/
5863
toObject(): any {
5964
const result: any = super.toObject();
60-
result.type = 'typeParameter';
6165
result.name = this.name;
6266

6367
if (this.constraint) {

src/lib/models/types/union.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export class UnionType extends Type {
1313
*/
1414
types: Type[];
1515

16+
/**
17+
* The type name identifier.
18+
*/
19+
readonly type: string = 'union';
20+
1621
/**
1722
* Create a new TupleType instance.
1823
*
@@ -55,7 +60,6 @@ export class UnionType extends Type {
5560
*/
5661
toObject(): any {
5762
const result: any = super.toObject();
58-
result.type = 'union';
5963

6064
if (this.types && this.types.length) {
6165
result.types = this.types.map((e) => e.toObject());

src/lib/models/types/unknown.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ export class UnknownType extends Type {
99
*/
1010
name: string;
1111

12+
/**
13+
* The type name identifier.
14+
*/
15+
readonly type: string = 'unknown';
16+
1217
/**
1318
* Create a new instance of UnknownType.
1419
*
@@ -47,7 +52,6 @@ export class UnknownType extends Type {
4752
*/
4853
toObject(): any {
4954
const result: any = super.toObject();
50-
result.type = 'unknown';
5155
result.name = this.name;
5256
return result;
5357
}

0 commit comments

Comments
 (0)