Skip to content

Commit 0a61a00

Browse files
committed
Add type predicate converter
Also add a few more tests + rebuild specs again.
1 parent 1f01579 commit 0a61a00

File tree

11 files changed

+438
-64
lines changed

11 files changed

+438
-64
lines changed

scripts/copy_test_files.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ const copy = [
1313
const copies = copy.map(dir => {
1414
const source = join(__dirname, '../src', dir);
1515
const target = join(__dirname, '../dist', dir);
16-
return fs.mkdirp(target)
16+
return fs.remove(target)
17+
.then(() => fs.mkdirp(target))
1718
.then(() => fs.copy(source, target));
1819
})
1920

src/lib/converter/converter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,12 @@ export class Converter extends ChildableComponent<Application, ConverterComponen
218218
private addTypeConverter(converter: ConverterTypeComponent) {
219219
if ('supportsNode' in converter && 'convertNode' in converter) {
220220
this.typeNodeConverters.push(<TypeNodeConverter<any, any>> converter);
221-
this.typeNodeConverters.sort((a, b) => (b.priority || 0) - (a.priority || 0));
221+
this.typeNodeConverters.sort((a, b) => b.priority - a.priority);
222222
}
223223

224224
if ('supportsType' in converter && 'convertType' in converter) {
225225
this.typeTypeConverters.push(<TypeTypeConverter<any>> converter);
226-
this.typeTypeConverters.sort((a, b) => (b.priority || 0) - (a.priority || 0));
226+
this.typeTypeConverters.sort((a, b) => b.priority - a.priority);
227227
}
228228
}
229229

src/lib/converter/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export { BindingArrayConverter } from './binding-array';
44
export { BindingObjectConverter } from './binding-object';
55
export { EnumConverter } from './enum';
66
export { IntrinsicConverter } from './intrinsic';
7+
export { PredicateConverter } from './predicate';
78
export { StringLiteralConverter } from './string-literal';
89
export { ReferenceConverter } from './reference';
910
export { ThisConverter } from './this';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import * as ts from 'typescript';
2+
3+
import { PredicateType } from '../../models/types/index';
4+
import { Component, ConverterTypeComponent } from '../components';
5+
import { Context } from '../context';
6+
7+
@Component({ name: 'type:predicate' })
8+
export class PredicateConverter extends ConverterTypeComponent {
9+
/**
10+
* This must run before the base `Type` converter.
11+
*/
12+
priority = 50;
13+
14+
/**
15+
* Test whether this converter can handle the given TypeScript node.
16+
*/
17+
supportsNode(_context: Context, node: ts.Node): boolean {
18+
return ts.isTypePredicateNode(node);
19+
}
20+
21+
/**
22+
* Convert the given predicate type node to its type reflection.
23+
*/
24+
convertNode(context: Context, node: ts.TypePredicateNode): PredicateType {
25+
const name = ts.isThisTypeNode(node.parameterName) ? 'this' : node.parameterName.getText();
26+
const asserts = !!node.assertsModifier;
27+
const targetType = this.owner.convertType(context, node.type);
28+
return new PredicateType(name, asserts, targetType);
29+
}
30+
}

src/lib/models/types/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
export { Type } from './abstract';
22
export { ArrayType } from './array';
3-
export { IntrinsicType } from './intrinsic';
43
export { IntersectionType } from './intersection';
4+
export { IntrinsicType } from './intrinsic';
5+
export { PredicateType } from './predicate';
56
export { ReferenceType } from './reference';
67
export { ReflectionType } from './reflection';
78
export { StringLiteralType } from './string-literal';

src/lib/models/types/predicate.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { Type } from './abstract';
2+
3+
/**
4+
* Represents a type predicate.
5+
*
6+
* ```ts
7+
* function isString(anything: any): anything is string {}
8+
* function assert(condition: boolean): asserts condition {}
9+
* ```
10+
*/
11+
export class PredicateType extends Type {
12+
/**
13+
* The type that the identifier is tested to be.
14+
* May be undefined if the type is of the form `asserts val`.
15+
* Will be defined if the type is of the form `asserts val is string` or `val is string`.
16+
*/
17+
targetType?: Type;
18+
19+
/**
20+
* The identifier name which is tested by the predicate.
21+
*/
22+
name: string;
23+
24+
/**
25+
* True if the type is of the form `asserts val is string`, false if
26+
* the type is of the form `val is string`
27+
*/
28+
asserts: boolean;
29+
30+
/**
31+
* The type name identifier.
32+
*/
33+
readonly type = 'predicate';
34+
35+
/**
36+
* Create a new PredicateType instance.
37+
*/
38+
constructor(name: string, asserts: boolean, targetType?: Type) {
39+
super();
40+
this.name = name;
41+
this.asserts = asserts;
42+
this.targetType = targetType;
43+
}
44+
45+
/**
46+
* Clone this type.
47+
*
48+
* @return A clone of this type.
49+
*/
50+
clone(): Type {
51+
return new PredicateType(this.name, this.asserts, this.targetType);
52+
}
53+
54+
/**
55+
* Test whether this type equals the given type.
56+
*
57+
* @param type The type that should be checked for equality.
58+
* @returns TRUE if the given type equals this type, FALSE otherwise.
59+
*/
60+
equals(type: Type): boolean {
61+
if (!(type instanceof PredicateType)) {
62+
return false;
63+
}
64+
65+
if (!this.targetType && type.targetType) {
66+
return false;
67+
}
68+
if (this.targetType && !type.targetType) {
69+
return false;
70+
}
71+
72+
return this.name === type.name
73+
&& this.asserts === type.asserts
74+
&& (this.targetType?.equals(type.targetType!) ?? true);
75+
}
76+
77+
/**
78+
* Return a raw object representation of this type.
79+
* @deprecated Use serializers instead
80+
*/
81+
toObject(): any {
82+
return {
83+
...super.toObject(),
84+
name: this.name,
85+
asserts: this.asserts,
86+
targetType: this.targetType
87+
};
88+
}
89+
90+
/**
91+
* Return a string representation of this type.
92+
*/
93+
toString() {
94+
const out = this.asserts ? ['asserts', this.name] : [this.name];
95+
if (this.targetType) {
96+
out.push('is', this.targetType.toString());
97+
}
98+
99+
return out.join(' ');
100+
}
101+
}

src/lib/serialization/serializers/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from './abstract';
22
export * from './array';
33
export * from './intersection-union';
44
export * from './intrinsic';
5+
export * from './predicate';
56
export * from './reference';
67
export * from './reflection';
78
export * from './string-literal';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Component } from '../../../utils/component';
2+
import { PredicateType } from '../../../models';
3+
import { TypeSerializerComponent } from '../../components';
4+
5+
@Component({ name: 'serializer:predicate-type' })
6+
export class PredicateTypeSerializer extends TypeSerializerComponent<PredicateType> {
7+
supports(t: unknown) {
8+
return t instanceof PredicateType;
9+
}
10+
11+
toObject(type: PredicateType, obj?: any): any {
12+
return {
13+
...obj,
14+
name: type.name,
15+
asserts: type.asserts,
16+
targetType: type.targetType
17+
};
18+
}
19+
}

src/test/converter/function/function.ts

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
/**
22
* This is an internal function.
33
*/
4-
function internalFunction():void { }
5-
4+
function internalFunction(): void { }
65

76
/**
87
* This is a simple exported function.
98
*/
10-
export function exportedFunction():void { }
11-
9+
export function exportedFunction(): void { }
1210

1311
/**
1412
* This is a function with multiple arguments and a return value.
@@ -27,30 +25,27 @@ export function exportedFunction():void { }
2725
*
2826
* @returns This is the return value of the function.
2927
*/
30-
export function functionWithParameters(paramZ:string, paramG:any, paramA:Object):number {
28+
export function functionWithParameters(paramZ: string, paramG: any, paramA: Object): number {
3129
return 0;
3230
}
3331

34-
3532
/**
3633
* This is a function that is assigned to a variable.
3734
*
3835
* @param someParam This is some numeric parameter.
3936
* @return This is the return value of the function.
4037
*/
41-
export const variableFunction = function(someParam:number):number {
38+
export const variableFunction = function(someParam: number): number {
4239
return 0;
4340
};
4441

45-
4642
/**
4743
* This is a function with a parameter that is optional.
4844
*
4945
* @param requiredParam A normal parameter.
5046
* @param optionalParam An optional parameter.
5147
*/
52-
export function functionWithOptionalValue(requiredParam:string, optionalParam?:string) { }
53-
48+
export function functionWithOptionalValue(requiredParam: string, optionalParam?: string) { }
5449

5550
/**
5651
* This is a function with a parameter that has a default value.
@@ -63,51 +58,49 @@ export function functionWithOptionalValue(requiredParam:string, optionalParam?:s
6358
* @return This is the return value of the function.
6459
*/
6560
export function functionWithDefaults(
66-
valueA:string = 'defaultValue',
67-
valueB:number = 100,
68-
valueC:number = Number.NaN,
69-
valueD:boolean = true,
70-
valueE:boolean = null
71-
):string {
61+
valueA: string = 'defaultValue',
62+
valueB: number = 100,
63+
valueC: number = Number.NaN,
64+
valueD: boolean = true,
65+
valueE: boolean = null
66+
): string {
7267
return valueA;
7368
}
7469

75-
7670
/**
7771
* This is a function with rest parameter.
7872
*
7973
* @param rest The rest parameter.
8074
* @return This is the return value of the function.
8175
*/
82-
function functionWithRest(...rest:string[]):string {
76+
function functionWithRest(...rest: string[]): string {
8377
return rest.join(', ');
8478
}
8579

86-
8780
/**
8881
* This is the first signature of a function with multiple signatures.
8982
*
9083
* @param value The name value.
9184
*/
92-
export function multipleSignatures(value:string):string;
85+
export function multipleSignatures(value: string): string;
9386

9487
/**
9588
* This is the second signature of a function with multiple signatures.
9689
*
9790
* @param value An object containing the name value.
9891
* @param value.name A value of the object.
9992
*/
100-
export function multipleSignatures(value:{name:string}):string;
93+
export function multipleSignatures(value: {name: string}): string;
10194

10295
/**
10396
* This is the actual implementation, this comment will not be visible
10497
* in the generated documentation.
10598
*
10699
* @return This is the return value of the function.
107100
*/
108-
export function multipleSignatures():string {
101+
export function multipleSignatures(): string {
109102
if (arguments.length > 0) {
110-
if (typeof arguments[0] == 'object') {
103+
if (typeof arguments[0] === 'object') {
111104
return arguments[0].name;
112105
} else {
113106
return arguments[0];
@@ -117,33 +110,54 @@ export function multipleSignatures():string {
117110
return '';
118111
}
119112

120-
121113
/**
122114
* This is a function that is extended by a module.
123115
*
124116
* @param arg An argument.
125117
*/
126-
export function moduleFunction(arg:string):string { return ''; }
127-
118+
export function moduleFunction(arg: string): string { return ''; }
128119

129120
/**
130121
* This is an assertion function.
131122
*
132123
* @param condition The condition that is asserted to be true when this function returns.
133124
*/
134-
export function assertionFunction(condition:boolean):asserts condition { }
125+
export function assertionFunction(condition: boolean): asserts condition { }
126+
127+
/**
128+
* Assertion function with a type.
129+
* @param anything
130+
*/
131+
export function checkerFunction(anything: any): anything is string {
132+
return typeof anything === 'string';
133+
}
135134

135+
/**
136+
* Asserts that an argument is not null.
137+
* @param arg
138+
*/
139+
export function assertIsNonNull<T>(arg: T | null | undefined): asserts arg is T {
140+
if (arg == null) {
141+
throw new Error('Was nullable');
142+
}
143+
}
144+
145+
/**
146+
* Checks that an argument is not null.
147+
* @param arg
148+
*/
149+
export function isNonNull<T>(arg: T | null | undefined): arg is T {
150+
return arg != null;
151+
}
136152

137153
/**
138154
* This is the module extending the function moduleFunction().
139155
*/
140-
export module moduleFunction
141-
{
156+
export module moduleFunction {
142157
/**
143158
* This variable is appended to a function.
144159
*/
145-
let functionVariable:string;
146-
160+
let functionVariable: string;
147161

148162
/**
149163
* This function is appended to another function.

0 commit comments

Comments
 (0)