Skip to content

Commit 49eec37

Browse files
Merge branch '6606-type-level-function-application' into 5453-type-call-spreads
2 parents cd2ea9a + a76ba38 commit 49eec37

21 files changed

+774
-43
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6799,7 +6799,7 @@ namespace ts {
67996799
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, node));
68006800
return createTypeReference(<GenericType>type, typeArguments);
68016801
}
6802-
if (node.typeArguments) {
6802+
if (node.typeArguments && node.typeArguments.length) {
68036803
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
68046804
return unknownType;
68056805
}
@@ -6841,7 +6841,7 @@ namespace ts {
68416841
}
68426842
return getTypeAliasInstantiation(symbol, typeArguments);
68436843
}
6844-
if (node.typeArguments) {
6844+
if (node.typeArguments && node.typeArguments.length) {
68456845
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
68466846
return unknownType;
68476847
}
@@ -6852,7 +6852,7 @@ namespace ts {
68526852
* Get type from reference to named type that cannot be generic (enum or type parameter)
68536853
*/
68546854
function getTypeFromNonGenericTypeReference(node: TypeReferenceType, symbol: Symbol): Type {
6855-
if (node.typeArguments) {
6855+
if (node.typeArguments && node.typeArguments.length) {
68566856
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
68576857
return unknownType;
68586858
}
@@ -7553,6 +7553,18 @@ namespace ts {
75537553
return links.resolvedType;
75547554
}
75557555

7556+
function getTypeFromTypeCallNode(node: TypeCallTypeNode): Type {
7557+
const fn = typeNodeToExpression(node.type);
7558+
const args = map(node.arguments, typeNodeToExpression);
7559+
const callExpr = createCall(fn, node.typeArguments, args);
7560+
return checkExpression(callExpr);
7561+
}
7562+
7563+
// null! as type
7564+
function typeNodeToExpression(type: TypeNode): Expression {
7565+
return createAsExpression(createNonNullExpression(createNull()), type);
7566+
}
7567+
75567568
function createIndexedAccessType(objectType: Type, indexType: Type) {
75577569
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
75587570
type.objectType = objectType;
@@ -8010,6 +8022,8 @@ namespace ts {
80108022
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
80118023
case SyntaxKind.TypeOperator:
80128024
return getTypeFromTypeOperatorNode(<TypeOperatorNode>node);
8025+
case SyntaxKind.TypeCall:
8026+
return getTypeFromTypeCallNode(<TypeCallTypeNode>node);
80138027
case SyntaxKind.IndexedAccessType:
80148028
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
80158029
case SyntaxKind.MappedType:
@@ -13247,7 +13261,7 @@ namespace ts {
1324713261
return node.contextualType;
1324813262
}
1324913263
const parent = node.parent;
13250-
switch (parent.kind) {
13264+
switch (parent && parent.kind) {
1325113265
case SyntaxKind.VariableDeclaration:
1325213266
case SyntaxKind.Parameter:
1325313267
case SyntaxKind.PropertyDeclaration:
@@ -18782,7 +18796,7 @@ namespace ts {
1878218796
}
1878318797
const type = getTypeFromTypeReference(node);
1878418798
if (type !== unknownType) {
18785-
if (node.typeArguments) {
18799+
if (node.typeArguments && node.typeArguments.length) {
1878618800
// Do type argument local checks only if referenced type is successfully resolved
1878718801
forEach(node.typeArguments, checkSourceElement);
1878818802
if (produceDiagnostics) {
@@ -24338,7 +24352,7 @@ namespace ts {
2433824352

2433924353
function checkGrammarTypeArguments(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
2434024354
return checkGrammarForDisallowedTrailingComma(typeArguments) ||
24341-
checkGrammarForAtLeastOneTypeArgument(node, typeArguments);
24355+
false && checkGrammarForAtLeastOneTypeArgument(node, typeArguments);
2434224356
}
2434324357

2434424358
function checkGrammarForOmittedArgument(args: NodeArray<Expression>): boolean {

src/compiler/emitter.ts

Lines changed: 8 additions & 0 deletions
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

Lines changed: 16 additions & 0 deletions
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

Lines changed: 49 additions & 5 deletions
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) ||
@@ -2739,8 +2743,8 @@ namespace ts {
27392743
return token() === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType();
27402744
}
27412745

2742-
function parseJSDocPostfixTypeOrHigher(): TypeNode {
2743-
const type = parseNonArrayType();
2746+
function parseJSDocPostfixTypeOrHigher(typeNode?: TypeNode): TypeNode {
2747+
const type = typeNode || parseNonArrayType();
27442748
const kind = getKind(token());
27452749
if (!kind) return type;
27462750
nextToken();
@@ -2762,8 +2766,8 @@ namespace ts {
27622766
}
27632767
}
27642768

2765-
function parseArrayTypeOrHigher(): TypeNode {
2766-
let type = parseJSDocPostfixTypeOrHigher();
2769+
function parseArrayTypeOrHigher(typeNode?: TypeNode): TypeNode {
2770+
let type = parseJSDocPostfixTypeOrHigher(typeNode);
27672771
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
27682772
if (isStartOfType()) {
27692773
const node = <IndexedAccessTypeNode>createNode(SyntaxKind.IndexedAccessType, type.pos);
@@ -2795,7 +2799,7 @@ namespace ts {
27952799
case SyntaxKind.KeyOfKeyword:
27962800
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
27972801
}
2798-
return parseArrayTypeOrHigher();
2802+
return parseTypeCallRest();
27992803
}
28002804

28012805
function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode {
@@ -4241,6 +4245,46 @@ namespace ts {
42414245
}
42424246
}
42434247

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

src/compiler/types.ts

Lines changed: 9 additions & 1 deletion
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

Lines changed: 10 additions & 0 deletions
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);
Lines changed: 5 additions & 17 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 9 additions & 3 deletions
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

Lines changed: 2 additions & 1 deletion
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;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//// [overloadSelection.ts]
2+
interface Match {
3+
(o: object): 0;
4+
(o: any): 1;
5+
}
6+
type Wrap = <T = RegExp>(v: T) => Match(T);
7+
type A = Wrap(RegExp);
8+
// falls thru to 1, `object` checked not with generic val `RegExp` but with its constraint (generic `any`)
9+
10+
11+
//// [overloadSelection.js]
12+
// falls thru to 1, `object` checked not with generic val `RegExp` but with its constraint (generic `any`)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/compiler/overloadSelection.ts ===
2+
interface Match {
3+
>Match : Symbol(Match, Decl(overloadSelection.ts, 0, 0))
4+
5+
(o: object): 0;
6+
>o : Symbol(o, Decl(overloadSelection.ts, 1, 3))
7+
8+
(o: any): 1;
9+
>o : Symbol(o, Decl(overloadSelection.ts, 2, 3))
10+
}
11+
type Wrap = <T = RegExp>(v: T) => Match(T);
12+
>Wrap : Symbol(Wrap, Decl(overloadSelection.ts, 3, 1))
13+
>T : Symbol(T, Decl(overloadSelection.ts, 4, 13))
14+
>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
15+
>v : Symbol(v, Decl(overloadSelection.ts, 4, 25))
16+
>T : Symbol(T, Decl(overloadSelection.ts, 4, 13))
17+
>Match : Symbol(Match, Decl(overloadSelection.ts, 0, 0))
18+
>T : Symbol(T, Decl(overloadSelection.ts, 4, 13))
19+
20+
type A = Wrap(RegExp);
21+
>A : Symbol(A, Decl(overloadSelection.ts, 4, 43))
22+
>Wrap : Symbol(Wrap, Decl(overloadSelection.ts, 3, 1))
23+
>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
24+
25+
// falls thru to 1, `object` checked not with generic val `RegExp` but with its constraint (generic `any`)
26+

0 commit comments

Comments
 (0)