Skip to content

Commit

Permalink
add type-level function application
Browse files Browse the repository at this point in the history
  • Loading branch information
KiaraGrouwstra committed Aug 22, 2017
1 parent 2b10784 commit 080b391
Show file tree
Hide file tree
Showing 17 changed files with 373 additions and 38 deletions.
16 changes: 15 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7546,6 +7546,18 @@ namespace ts {
return links.resolvedType;
}

function getTypeFromTypeCallNode(node: TypeCallTypeNode): Type {
const fn = typeToExpression(node.type);
const args = map(node.arguments, typeToExpression);
const callExpr = createCall(fn, node.typeArguments, args);
return checkExpression(callExpr);
}

// null! as type
function typeToExpression(type: TypeNode): Expression {
return createAsExpression(createNonNullExpression(createNull()), type);
}

function createIndexedAccessType(objectType: Type, indexType: Type) {
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
type.objectType = objectType;
Expand Down Expand Up @@ -8003,6 +8015,8 @@ namespace ts {
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
case SyntaxKind.TypeOperator:
return getTypeFromTypeOperatorNode(<TypeOperatorNode>node);
case SyntaxKind.TypeCall:
return getTypeFromTypeCallNode(<TypeCallTypeNode>node);
case SyntaxKind.IndexedAccessType:
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
case SyntaxKind.MappedType:
Expand Down Expand Up @@ -13179,7 +13193,7 @@ namespace ts {
return node.contextualType;
}
const parent = node.parent;
switch (parent.kind) {
switch (parent && parent.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ namespace ts {
return emitPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return emitElementAccessExpression(<ElementAccessExpression>node);
case SyntaxKind.TypeCall:
return emitTypeCall(<TypeCallTypeNode>node);
case SyntaxKind.CallExpression:
return emitCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
Expand Down Expand Up @@ -1240,6 +1242,12 @@ namespace ts {
write("]");
}

function emitTypeCall(node: TypeCallTypeNode) {
emit(node.type);
emitTypeArguments(node, node.typeArguments);
emitList(node, node.arguments, ListFormat.CallExpressionArguments);
}

function emitCallExpression(node: CallExpression) {
emitExpression(node.expression);
emitTypeArguments(node, node.typeArguments);
Expand Down
16 changes: 16 additions & 0 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,22 @@ namespace ts {
: node;
}

export function createTypeCall(type: TypeNode, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<TypeNode>) {
const node = <TypeCallTypeNode>createSynthesizedNode(SyntaxKind.TypeCall);
node.type = parenthesizeElementTypeMember(type);
node.typeArguments = asNodeArray(typeArguments);
node.arguments = parenthesizeElementTypeMembers(createNodeArray(argumentsArray));
return node;
}

export function updateTypeCall(node: TypeCallTypeNode, type: TypeNode, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<TypeNode>) {
return node.type !== type
|| node.typeArguments !== typeArguments
|| node.arguments !== argumentsArray
? updateNode(createTypeCall(type, typeArguments, argumentsArray), node)
: node;
}

export function createCall(expression: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<Expression>) {
const node = <CallExpression>createSynthesizedNode(SyntaxKind.CallExpression);
node.expression = parenthesizeForAccess(expression);
Expand Down
54 changes: 49 additions & 5 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ namespace ts {
case SyntaxKind.ElementAccessExpression:
return visitNode(cbNode, (<ElementAccessExpression>node).expression) ||
visitNode(cbNode, (<ElementAccessExpression>node).argumentExpression);
case SyntaxKind.TypeCall:
return visitNode(cbNode, (<TypeCallTypeNode>node).type) ||
visitNodes(cbNode, cbNodes, (<TypeCallTypeNode>node).typeArguments) ||
visitNodes(cbNode, cbNodes, (<TypeCallTypeNode>node).arguments);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return visitNode(cbNode, (<CallExpression>node).expression) ||
Expand Down Expand Up @@ -2738,8 +2742,8 @@ namespace ts {
return token() === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType();
}

function parseJSDocPostfixTypeOrHigher(): TypeNode {
const type = parseNonArrayType();
function parseJSDocPostfixTypeOrHigher(typeNode?: TypeNode): TypeNode {
const type = typeNode || parseNonArrayType();
const kind = getKind(token());
if (!kind) return type;
nextToken();
Expand All @@ -2761,8 +2765,8 @@ namespace ts {
}
}

function parseArrayTypeOrHigher(): TypeNode {
let type = parseJSDocPostfixTypeOrHigher();
function parseArrayTypeOrHigher(typeNode?: TypeNode): TypeNode {
let type = parseJSDocPostfixTypeOrHigher(typeNode);
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
if (isStartOfType()) {
const node = <IndexedAccessTypeNode>createNode(SyntaxKind.IndexedAccessType, type.pos);
Expand Down Expand Up @@ -2794,7 +2798,7 @@ namespace ts {
case SyntaxKind.KeyOfKeyword:
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
}
return parseArrayTypeOrHigher();
return parseTypeCallRest();
}

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

// type equivalent of parseCallExpressionRest
function parseTypeCallRest(type?: TypeNode): TypeNode {
while (true) {
type = parseArrayTypeOrHigher(type);
if (token() === SyntaxKind.LessThanToken) {
// See if this is the start of a generic invocation. If so, consume it and
// keep checking for postfix expressions. Otherwise, it's just a '<' that's
// part of an arithmetic expression. Break out so we consume it higher in the
// stack.
const typeArguments = tryParse(parseTypeArgumentsInExpression);
if (!typeArguments) {
return type;
}

const callExpr = <TypeCallTypeNode>createNode(SyntaxKind.TypeCall, type.pos);
callExpr.type = type;
callExpr.typeArguments = typeArguments;
callExpr.arguments = parseTypeArgumentList();
type = finishNode(callExpr);
continue;
}
else if (token() === SyntaxKind.OpenParenToken) {
const callExpr = <TypeCallTypeNode>createNode(SyntaxKind.TypeCall, type.pos);
callExpr.type = type;
callExpr.arguments = parseTypeArgumentList();
type = finishNode(callExpr);
continue;
}

return type;
}
}

function parseTypeArgumentList() {
parseExpected(SyntaxKind.OpenParenToken);
const result = parseDelimitedList(ParsingContext.TypeArguments, parseType);
parseExpected(SyntaxKind.CloseParenToken);
return result;
}

function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression {
while (true) {
expression = parseMemberExpressionRest(expression);
Expand Down
10 changes: 9 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ namespace ts {
IndexedAccessType,
MappedType,
LiteralType,
TypeCall,
// Binding patterns
ObjectBindingPattern,
ArrayBindingPattern,
Expand Down Expand Up @@ -398,7 +399,7 @@ namespace ts {
FirstFutureReservedWord = ImplementsKeyword,
LastFutureReservedWord = YieldKeyword,
FirstTypeNode = TypePredicate,
LastTypeNode = LiteralType,
LastTypeNode = TypeCall,
FirstPunctuation = OpenBraceToken,
LastPunctuation = CaretEqualsToken,
FirstToken = Unknown,
Expand Down Expand Up @@ -1495,6 +1496,13 @@ namespace ts {
arguments: NodeArray<Expression>;
}

export interface TypeCallTypeNode extends TypeNode {
kind: SyntaxKind.TypeCall;
type: TypeNode;
typeArguments?: NodeArray<TypeNode>;
arguments: NodeArray<TypeNode>;
}

// see: https://tc39.github.io/ecma262/#prod-SuperCall
export interface SuperCall extends CallExpression {
expression: SuperExpression;
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,12 @@ namespace ts {
visitNode((<ElementAccessExpression>node).expression, visitor, isExpression),
visitNode((<ElementAccessExpression>node).argumentExpression, visitor, isExpression));

case SyntaxKind.TypeCall:
return updateTypeCall(<TypeCallTypeNode>node,
visitNode((<TypeCallTypeNode>node).type, visitor, isTypeNode),
nodesVisitor((<TypeCallTypeNode>node).typeArguments, visitor, isTypeNode),
nodesVisitor((<TypeCallTypeNode>node).arguments, visitor, isTypeNode));

case SyntaxKind.CallExpression:
return updateCall(<CallExpression>node,
visitNode((<CallExpression>node).expression, visitor, isExpression),
Expand Down Expand Up @@ -1391,6 +1397,10 @@ namespace ts {
result = reduceNode((<SpreadAssignment>node).expression, cbNode, result);
break;

case SyntaxKind.TypeCall:
result = reduceNode((<TypeCallTypeNode>node).type, cbNode, result);
break;

// Enum
case SyntaxKind.EnumMember:
result = reduceNode((<EnumMember>node).name, cbNode, result);
Expand Down
22 changes: 5 additions & 17 deletions tests/baselines/reference/arrayTypeOfTypeOf.errors.txt
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,5): error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,22): error TS1005: '=' expected.
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,30): error TS1109: Expression expected.
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,5): error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,22): error TS1005: '=' expected.
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,32): error TS1109: Expression expected.
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(6,30): error TS1005: '(' expected.
tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts(7,32): error TS1005: '(' expected.


==== tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts (6 errors) ====
==== tests/cases/conformance/types/specifyingTypes/typeLiterals/arrayTypeOfTypeOf.ts (2 errors) ====
// array type cannot use typeof.

var x = 1;
var xs: typeof x[]; // Not an error. This is equivalent to Array<typeof x>
var xs2: typeof Array;
var xs3: typeof Array<number>;
~~~
!!! error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
~
!!! error TS1005: '=' expected.
~
!!! error TS1109: Expression expected.
!!! error TS1005: '(' expected.
var xs4: typeof Array<typeof x>;
~~~
!!! error TS2322: Type 'number' is not assignable to type 'ArrayConstructor'.
~
!!! error TS1005: '=' expected.
~
!!! error TS1109: Expression expected.
!!! error TS1005: '(' expected.
4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayTypeOfTypeOf.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ var xs4: typeof Array<typeof x>;
var x = 1;
var xs; // Not an error. This is equivalent to Array<typeof x>
var xs2;
var xs3 = ;
var xs4 = ;
var xs3;
var xs4;
12 changes: 9 additions & 3 deletions tests/baselines/reference/invalidTypeOfTarget.errors.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(1,16): error TS1003: Identifier expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,16): error TS1003: Identifier expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,24): error TS1005: '=>' expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,18): error TS1005: ',' expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,20): error TS1134: Variable declaration expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(2,24): error TS1109: Expression expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(3,16): error TS1003: Identifier expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(4,16): error TS1003: Identifier expected.
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(5,16): error TS1003: Identifier expected.
Expand All @@ -12,15 +14,19 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts
tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts(8,16): error TS1003: Identifier expected.


==== tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts (12 errors) ====
==== tests/cases/conformance/types/specifyingTypes/typeQueries/invalidTypeOfTarget.ts (14 errors) ====
var x1: typeof {};
~
!!! error TS1003: Identifier expected.
var x2: typeof (): void;
~
!!! error TS1003: Identifier expected.
~
!!! error TS1005: ',' expected.
~~~~
!!! error TS1134: Variable declaration expected.
~
!!! error TS1005: '=>' expected.
!!! error TS1109: Expression expected.
var x3: typeof 1;
~
!!! error TS1003: Identifier expected.
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/invalidTypeOfTarget.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ var x8: typeof /123/;

//// [invalidTypeOfTarget.js]
var x1 = {};
var x2 = function () { return ; };
var x2;
void ;
var x3 = 1;
var x4 = '';
var x5;
Expand Down
5 changes: 4 additions & 1 deletion tests/baselines/reference/parserObjectType5.errors.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts(2,7): error TS2304: Cannot find name 'B'.
tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts(3,5): error TS2304: Cannot find name 'T'.
tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts(3,7): error TS1005: '(' expected.


==== tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts (2 errors) ====
==== tests/cases/conformance/parser/ecmascript5/ObjectTypes/parserObjectType5.ts (3 errors) ====
var v: {
A: B
~
!!! error TS2304: Cannot find name 'B'.
<T>;
~
!!! error TS2304: Cannot find name 'T'.
~
!!! error TS1005: '(' expected.
};
9 changes: 3 additions & 6 deletions tests/baselines/reference/parserTypeQuery8.errors.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,15): error TS2304: Cannot find name 'A'.
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,16): error TS1005: '=' expected.
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,17): error TS2304: Cannot find name 'B'.
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,19): error TS1109: Expression expected.
tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts(1,19): error TS1005: '(' expected.


==== tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts (4 errors) ====
==== tests/cases/conformance/parser/ecmascript5/Types/parserTypeQuery8.ts (3 errors) ====
var v: typeof A<B>
~
!!! error TS2304: Cannot find name 'A'.
~
!!! error TS1005: '=' expected.
~
!!! error TS2304: Cannot find name 'B'.

!!! error TS1109: Expression expected.
!!! error TS1005: '(' expected.
2 changes: 1 addition & 1 deletion tests/baselines/reference/parserTypeQuery8.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
var v: typeof A<B>

//// [parserTypeQuery8.js]
var v = ;
var v;
36 changes: 36 additions & 0 deletions tests/baselines/reference/typeCall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//// [typeCall.ts]
type F1 = () => 1;
type a = F1();

type F2 = (a: string) => 1;
type b = F2('foo');

interface F3 {
(): 1;
(a: number): 2;
(a: string): 3;
}
type c = F3();
type d = F3(123);
type e = F3('foo');

declare function f4(a: string): 1;
let a = 'foo';
type f = typeof f4(typeof a);

type g = (() => 1)();

type Id = <T>(v: T) => T;
type h = Id(123);

type Wrap<T> = Id(T);
type i = Wrap<123>;

type F5 = () => () => { a: () => 1; };
type j = F5()()['a']();

type k = Id<string>('foo');


//// [typeCall.js]
var a = 'foo';
Loading

0 comments on commit 080b391

Please sign in to comment.