Skip to content

Commit 080b391

Browse files
add type-level function application
1 parent 2b10784 commit 080b391

17 files changed

+373
-38
lines changed

src/compiler/checker.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -7546,6 +7546,18 @@ namespace ts {
75467546
return links.resolvedType;
75477547
}
75487548

7549+
function getTypeFromTypeCallNode(node: TypeCallTypeNode): Type {
7550+
const fn = typeToExpression(node.type);
7551+
const args = map(node.arguments, typeToExpression);
7552+
const callExpr = createCall(fn, node.typeArguments, args);
7553+
return checkExpression(callExpr);
7554+
}
7555+
7556+
// null! as type
7557+
function typeToExpression(type: TypeNode): Expression {
7558+
return createAsExpression(createNonNullExpression(createNull()), type);
7559+
}
7560+
75497561
function createIndexedAccessType(objectType: Type, indexType: Type) {
75507562
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
75517563
type.objectType = objectType;
@@ -8003,6 +8015,8 @@ namespace ts {
80038015
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
80048016
case SyntaxKind.TypeOperator:
80058017
return getTypeFromTypeOperatorNode(<TypeOperatorNode>node);
8018+
case SyntaxKind.TypeCall:
8019+
return getTypeFromTypeCallNode(<TypeCallTypeNode>node);
80068020
case SyntaxKind.IndexedAccessType:
80078021
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
80088022
case SyntaxKind.MappedType:
@@ -13179,7 +13193,7 @@ namespace ts {
1317913193
return node.contextualType;
1318013194
}
1318113195
const parent = node.parent;
13182-
switch (parent.kind) {
13196+
switch (parent && parent.kind) {
1318313197
case SyntaxKind.VariableDeclaration:
1318413198
case SyntaxKind.Parameter:
1318513199
case SyntaxKind.PropertyDeclaration:

src/compiler/emitter.ts

+8
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,8 @@ namespace ts {
753753
return emitPropertyAccessExpression(<PropertyAccessExpression>node);
754754
case SyntaxKind.ElementAccessExpression:
755755
return emitElementAccessExpression(<ElementAccessExpression>node);
756+
case SyntaxKind.TypeCall:
757+
return emitTypeCall(<TypeCallTypeNode>node);
756758
case SyntaxKind.CallExpression:
757759
return emitCallExpression(<CallExpression>node);
758760
case SyntaxKind.NewExpression:
@@ -1240,6 +1242,12 @@ namespace ts {
12401242
write("]");
12411243
}
12421244

1245+
function emitTypeCall(node: TypeCallTypeNode) {
1246+
emit(node.type);
1247+
emitTypeArguments(node, node.typeArguments);
1248+
emitList(node, node.arguments, ListFormat.CallExpressionArguments);
1249+
}
1250+
12431251
function emitCallExpression(node: CallExpression) {
12441252
emitExpression(node.expression);
12451253
emitTypeArguments(node, node.typeArguments);

src/compiler/factory.ts

+16
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,22 @@ namespace ts {
888888
: node;
889889
}
890890

891+
export function createTypeCall(type: TypeNode, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<TypeNode>) {
892+
const node = <TypeCallTypeNode>createSynthesizedNode(SyntaxKind.TypeCall);
893+
node.type = parenthesizeElementTypeMember(type);
894+
node.typeArguments = asNodeArray(typeArguments);
895+
node.arguments = parenthesizeElementTypeMembers(createNodeArray(argumentsArray));
896+
return node;
897+
}
898+
899+
export function updateTypeCall(node: TypeCallTypeNode, type: TypeNode, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<TypeNode>) {
900+
return node.type !== type
901+
|| node.typeArguments !== typeArguments
902+
|| node.arguments !== argumentsArray
903+
? updateNode(createTypeCall(type, typeArguments, argumentsArray), node)
904+
: node;
905+
}
906+
891907
export function createCall(expression: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<Expression>) {
892908
const node = <CallExpression>createSynthesizedNode(SyntaxKind.CallExpression);
893909
node.expression = parenthesizeForAccess(expression);

src/compiler/parser.ts

+49-5
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ namespace ts {
170170
case SyntaxKind.ElementAccessExpression:
171171
return visitNode(cbNode, (<ElementAccessExpression>node).expression) ||
172172
visitNode(cbNode, (<ElementAccessExpression>node).argumentExpression);
173+
case SyntaxKind.TypeCall:
174+
return visitNode(cbNode, (<TypeCallTypeNode>node).type) ||
175+
visitNodes(cbNode, cbNodes, (<TypeCallTypeNode>node).typeArguments) ||
176+
visitNodes(cbNode, cbNodes, (<TypeCallTypeNode>node).arguments);
173177
case SyntaxKind.CallExpression:
174178
case SyntaxKind.NewExpression:
175179
return visitNode(cbNode, (<CallExpression>node).expression) ||
@@ -2738,8 +2742,8 @@ namespace ts {
27382742
return token() === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType();
27392743
}
27402744

2741-
function parseJSDocPostfixTypeOrHigher(): TypeNode {
2742-
const type = parseNonArrayType();
2745+
function parseJSDocPostfixTypeOrHigher(typeNode?: TypeNode): TypeNode {
2746+
const type = typeNode || parseNonArrayType();
27432747
const kind = getKind(token());
27442748
if (!kind) return type;
27452749
nextToken();
@@ -2761,8 +2765,8 @@ namespace ts {
27612765
}
27622766
}
27632767

2764-
function parseArrayTypeOrHigher(): TypeNode {
2765-
let type = parseJSDocPostfixTypeOrHigher();
2768+
function parseArrayTypeOrHigher(typeNode?: TypeNode): TypeNode {
2769+
let type = parseJSDocPostfixTypeOrHigher(typeNode);
27662770
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
27672771
if (isStartOfType()) {
27682772
const node = <IndexedAccessTypeNode>createNode(SyntaxKind.IndexedAccessType, type.pos);
@@ -2794,7 +2798,7 @@ namespace ts {
27942798
case SyntaxKind.KeyOfKeyword:
27952799
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
27962800
}
2797-
return parseArrayTypeOrHigher();
2801+
return parseTypeCallRest();
27982802
}
27992803

28002804
function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode {
@@ -4240,6 +4244,46 @@ namespace ts {
42404244
}
42414245
}
42424246

4247+
// type equivalent of parseCallExpressionRest
4248+
function parseTypeCallRest(type?: TypeNode): TypeNode {
4249+
while (true) {
4250+
type = parseArrayTypeOrHigher(type);
4251+
if (token() === SyntaxKind.LessThanToken) {
4252+
// See if this is the start of a generic invocation. If so, consume it and
4253+
// keep checking for postfix expressions. Otherwise, it's just a '<' that's
4254+
// part of an arithmetic expression. Break out so we consume it higher in the
4255+
// stack.
4256+
const typeArguments = tryParse(parseTypeArgumentsInExpression);
4257+
if (!typeArguments) {
4258+
return type;
4259+
}
4260+
4261+
const callExpr = <TypeCallTypeNode>createNode(SyntaxKind.TypeCall, type.pos);
4262+
callExpr.type = type;
4263+
callExpr.typeArguments = typeArguments;
4264+
callExpr.arguments = parseTypeArgumentList();
4265+
type = finishNode(callExpr);
4266+
continue;
4267+
}
4268+
else if (token() === SyntaxKind.OpenParenToken) {
4269+
const callExpr = <TypeCallTypeNode>createNode(SyntaxKind.TypeCall, type.pos);
4270+
callExpr.type = type;
4271+
callExpr.arguments = parseTypeArgumentList();
4272+
type = finishNode(callExpr);
4273+
continue;
4274+
}
4275+
4276+
return type;
4277+
}
4278+
}
4279+
4280+
function parseTypeArgumentList() {
4281+
parseExpected(SyntaxKind.OpenParenToken);
4282+
const result = parseDelimitedList(ParsingContext.TypeArguments, parseType);
4283+
parseExpected(SyntaxKind.CloseParenToken);
4284+
return result;
4285+
}
4286+
42434287
function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression {
42444288
while (true) {
42454289
expression = parseMemberExpressionRest(expression);

src/compiler/types.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ namespace ts {
240240
IndexedAccessType,
241241
MappedType,
242242
LiteralType,
243+
TypeCall,
243244
// Binding patterns
244245
ObjectBindingPattern,
245246
ArrayBindingPattern,
@@ -398,7 +399,7 @@ namespace ts {
398399
FirstFutureReservedWord = ImplementsKeyword,
399400
LastFutureReservedWord = YieldKeyword,
400401
FirstTypeNode = TypePredicate,
401-
LastTypeNode = LiteralType,
402+
LastTypeNode = TypeCall,
402403
FirstPunctuation = OpenBraceToken,
403404
LastPunctuation = CaretEqualsToken,
404405
FirstToken = Unknown,
@@ -1495,6 +1496,13 @@ namespace ts {
14951496
arguments: NodeArray<Expression>;
14961497
}
14971498

1499+
export interface TypeCallTypeNode extends TypeNode {
1500+
kind: SyntaxKind.TypeCall;
1501+
type: TypeNode;
1502+
typeArguments?: NodeArray<TypeNode>;
1503+
arguments: NodeArray<TypeNode>;
1504+
}
1505+
14981506
// see: https://tc39.github.io/ecma262/#prod-SuperCall
14991507
export interface SuperCall extends CallExpression {
15001508
expression: SuperExpression;

src/compiler/visitor.ts

+10
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,12 @@ namespace ts {
446446
visitNode((<ElementAccessExpression>node).expression, visitor, isExpression),
447447
visitNode((<ElementAccessExpression>node).argumentExpression, visitor, isExpression));
448448

449+
case SyntaxKind.TypeCall:
450+
return updateTypeCall(<TypeCallTypeNode>node,
451+
visitNode((<TypeCallTypeNode>node).type, visitor, isTypeNode),
452+
nodesVisitor((<TypeCallTypeNode>node).typeArguments, visitor, isTypeNode),
453+
nodesVisitor((<TypeCallTypeNode>node).arguments, visitor, isTypeNode));
454+
449455
case SyntaxKind.CallExpression:
450456
return updateCall(<CallExpression>node,
451457
visitNode((<CallExpression>node).expression, visitor, isExpression),
@@ -1391,6 +1397,10 @@ namespace ts {
13911397
result = reduceNode((<SpreadAssignment>node).expression, cbNode, result);
13921398
break;
13931399

1400+
case SyntaxKind.TypeCall:
1401+
result = reduceNode((<TypeCallTypeNode>node).type, cbNode, result);
1402+
break;
1403+
13941404
// Enum
13951405
case SyntaxKind.EnumMember:
13961406
result = reduceNode((<EnumMember>node).name, cbNode, result);
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,16 @@
1-
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,5): error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
2-
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,22): error TS1005: '=' expected.
3-
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,30): error TS1109: Expression expected.
4-
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,5): error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
5-
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,22): error TS1005: '=' expected.
6-
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,32): error TS1109: Expression expected.
1+
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,30): error TS1005: '(' expected.
2+
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,32): error TS1005: '(' expected.
73

84

9-
==== tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts (6 errors) ====
5+
==== tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts (2 errors) ====
106
// array type cannot use typeof.
117

128
var x = 1;
139
var xs: typeof x[]; // Not an error. This is equivalent to Array<typeof x>
1410
var xs2: typeof Array;
1511
var xs3: typeof Array<number>;
16-
~~~
17-
!!! error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
18-
~
19-
!!! error TS1005: '=' expected.
2012
~
21-
!!! error TS1109: Expression expected.
13+
!!! error TS1005: '(' expected.
2214
var xs4: typeof Array<typeof x>;
23-
~~~
24-
!!! error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
25-
~
26-
!!! error TS1005: '=' expected.
2715
~
28-
!!! error TS1109: Expression expected.
16+
!!! error TS1005: '(' expected.

tests/baselines/reference/arrayTypeOfTypeOf.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ var xs4: typeof Array<typeof x>;
1212
var x = 1;
1313
var xs; // Not an error. This is equivalent to Array<typeof x>
1414
var xs2;
15-
var xs3 = ;
16-
var xs4 = ;
15+
var xs3;
16+
var xs4;

tests/baselines/reference/invalidTypeOfTarget.errors.txt

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(1,16): error TS1003: Identifier expected.
22
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,16): error TS1003: Identifier expected.
3-
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,24): error TS1005: '=>' expected.
3+
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,18): error TS1005: ',' expected.
4+
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,20): error TS1134: Variable declaration expected.
5+
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,24): error TS1109: Expression expected.
46
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(3,16): error TS1003: Identifier expected.
57
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(4,16): error TS1003: Identifier expected.
68
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(5,16): error TS1003: Identifier expected.
@@ -12,15 +14,19 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts
1214
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(8,16): error TS1003: Identifier expected.
1315

1416

15-
==== tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts (12 errors) ====
17+
==== tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts (14 errors) ====
1618
var x1: typeof {};
1719
~
1820
!!! error TS1003: Identifier expected.
1921
var x2: typeof (): void;
2022
~
2123
!!! error TS1003: Identifier expected.
24+
~
25+
!!! error TS1005: ',' expected.
26+
~~~~
27+
!!! error TS1134: Variable declaration expected.
2228
~
23-
!!! error TS1005: '=>' expected.
29+
!!! error TS1109: Expression expected.
2430
var x3: typeof 1;
2531
~
2632
!!! error TS1003: Identifier expected.

tests/baselines/reference/invalidTypeOfTarget.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ var x8: typeof /123/;
1010

1111
//// [invalidTypeOfTarget.js]
1212
var x1 = {};
13-
var x2 = function () { return ; };
13+
var x2;
14+
void ;
1415
var x3 = 1;
1516
var x4 = '';
1617
var x5;
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts(2,7): error TS2304: Cannot find name 'B'.
2+
tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts(3,5): error TS2304: Cannot find name 'T'.
23
tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts(3,7): error TS1005: '(' expected.
34

45

5-
==== tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts (2 errors) ====
6+
==== tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts (3 errors) ====
67
var v: {
78
A: B
89
~
910
!!! error TS2304: Cannot find name 'B'.
1011
<T>;
12+
~
13+
!!! error TS2304: Cannot find name 'T'.
1114
~
1215
!!! error TS1005: '(' expected.
1316
};
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,15): error TS2304: Cannot find name 'A'.
2-
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,16): error TS1005: '=' expected.
32
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,17): error TS2304: Cannot find name 'B'.
4-
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,19): error TS1109: Expression expected.
3+
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,19): error TS1005: '(' expected.
54

65

7-
==== tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts (4 errors) ====
6+
==== tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts (3 errors) ====
87
var v: typeof A<B>
98
~
109
!!! error TS2304: Cannot find name 'A'.
11-
~
12-
!!! error TS1005: '=' expected.
1310
~
1411
!!! error TS2304: Cannot find name 'B'.
1512

16-
!!! error TS1109: Expression expected.
13+
!!! error TS1005: '(' expected.

tests/baselines/reference/parserTypeQuery8.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
var v: typeof A<B>
33

44
//// [parserTypeQuery8.js]
5-
var v = ;
5+
var v;

tests/baselines/reference/typeCall.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [typeCall.ts]
2+
type F1 = () => 1;
3+
type a = F1();
4+
5+
type F2 = (a: string) => 1;
6+
type b = F2('foo');
7+
8+
interface F3 {
9+
(): 1;
10+
(a: number): 2;
11+
(a: string): 3;
12+
}
13+
type c = F3();
14+
type d = F3(123);
15+
type e = F3('foo');
16+
17+
declare function f4(a: string): 1;
18+
let a = 'foo';
19+
type f = typeof f4(typeof a);
20+
21+
type g = (() => 1)();
22+
23+
type Id = <T>(v: T) => T;
24+
type h = Id(123);
25+
26+
type Wrap<T> = Id(T);
27+
type i = Wrap<123>;
28+
29+
type F5 = () => () => { a: () => 1; };
30+
type j = F5()()['a']();
31+
32+
type k = Id<string>('foo');
33+
34+
35+
//// [typeCall.js]
36+
var a = 'foo';

0 commit comments

Comments
 (0)