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 4307195 commit ddc7c3b
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 7 deletions.
16 changes: 15 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7526,6 +7526,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 @@ -7985,6 +7997,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 @@ -13161,7 +13175,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
34 changes: 34 additions & 0 deletions tests/baselines/reference/typeCall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [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']();


//// [typeCall.js]
var a = 'foo';
82 changes: 82 additions & 0 deletions tests/baselines/reference/typeCall.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
=== tests/cases/compiler/typeCall.ts ===
type F1 = () => 1;
>F1 : Symbol(F1, Decl(typeCall.ts, 0, 0))

type a = F1();
>a : Symbol(a, Decl(typeCall.ts, 0, 18), Decl(typeCall.ts, 16, 3))
>F1 : Symbol(F1, Decl(typeCall.ts, 0, 0))

type F2 = (a: string) => 1;
>F2 : Symbol(F2, Decl(typeCall.ts, 1, 14))
>a : Symbol(a, Decl(typeCall.ts, 3, 11))

type b = F2('foo');
>b : Symbol(b, Decl(typeCall.ts, 3, 27))
>F2 : Symbol(F2, Decl(typeCall.ts, 1, 14))

interface F3 {
>F3 : Symbol(F3, Decl(typeCall.ts, 4, 19))

(): 1;
(a: number): 2;
>a : Symbol(a, Decl(typeCall.ts, 8, 5))

(a: string): 3;
>a : Symbol(a, Decl(typeCall.ts, 9, 5))
}
type c = F3();
>c : Symbol(c, Decl(typeCall.ts, 10, 1))
>F3 : Symbol(F3, Decl(typeCall.ts, 4, 19))

type d = F3(123);
>d : Symbol(d, Decl(typeCall.ts, 11, 14))
>F3 : Symbol(F3, Decl(typeCall.ts, 4, 19))

type e = F3('foo');
>e : Symbol(e, Decl(typeCall.ts, 12, 17))
>F3 : Symbol(F3, Decl(typeCall.ts, 4, 19))

declare function f4(a: string): 1;
>f4 : Symbol(f4, Decl(typeCall.ts, 13, 19))
>a : Symbol(a, Decl(typeCall.ts, 15, 20))

let a = 'foo';
>a : Symbol(a, Decl(typeCall.ts, 0, 18), Decl(typeCall.ts, 16, 3))

type f = typeof f4(typeof a);
>f : Symbol(f, Decl(typeCall.ts, 16, 14))
>f4 : Symbol(f4, Decl(typeCall.ts, 13, 19))
>a : Symbol(a, Decl(typeCall.ts, 0, 18), Decl(typeCall.ts, 16, 3))

type g = (() => 1)();
>g : Symbol(g, Decl(typeCall.ts, 17, 29))

type Id = <T>(v: T) => T;
>Id : Symbol(Id, Decl(typeCall.ts, 19, 21))
>T : Symbol(T, Decl(typeCall.ts, 21, 11))
>v : Symbol(v, Decl(typeCall.ts, 21, 14))
>T : Symbol(T, Decl(typeCall.ts, 21, 11))
>T : Symbol(T, Decl(typeCall.ts, 21, 11))

type h = Id(123);
>h : Symbol(h, Decl(typeCall.ts, 21, 25))
>Id : Symbol(Id, Decl(typeCall.ts, 19, 21))

type Wrap<T> = Id(T);
>Wrap : Symbol(Wrap, Decl(typeCall.ts, 22, 17))
>T : Symbol(T, Decl(typeCall.ts, 24, 10))
>Id : Symbol(Id, Decl(typeCall.ts, 19, 21))
>T : Symbol(T, Decl(typeCall.ts, 24, 10))

type i = Wrap<123>;
>i : Symbol(i, Decl(typeCall.ts, 24, 21))
>Wrap : Symbol(Wrap, Decl(typeCall.ts, 22, 17))

type F5 = () => () => { a: () => 1; };
>F5 : Symbol(F5, Decl(typeCall.ts, 25, 19))
>a : Symbol(a, Decl(typeCall.ts, 27, 23))

type j = F5()()['a']();
>j : Symbol(j, Decl(typeCall.ts, 27, 38))
>F5 : Symbol(F5, Decl(typeCall.ts, 25, 19))

Loading

0 comments on commit ddc7c3b

Please sign in to comment.