Skip to content

Commit 77f2a41

Browse files
committed
Support 'asserts' type predicates in control flow analysis
1 parent e89acb6 commit 77f2a41

File tree

8 files changed

+163
-139
lines changed

8 files changed

+163
-139
lines changed

src/compiler/binder.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,12 +1284,14 @@ namespace ts {
12841284
activeLabels!.pop();
12851285
}
12861286

1287-
function isDottedName(node: Expression) {
1288-
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression && isQualifiedName((<PropertyAccessExpression>node).expression);
1287+
function isDottedName(node: Expression): boolean {
1288+
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression);
12891289
}
12901290

12911291
function bindExpressionStatement(node: ExpressionStatement): void {
12921292
bind(node.expression);
1293+
// A top level call expression with a dotted function name and at least one argument
1294+
// is potentially an assertion and is therefore included in the control flow.
12931295
if (node.expression.kind === SyntaxKind.CallExpression) {
12941296
const call = <CallExpression>node.expression;
12951297
if (isDottedName(call.expression) && call.arguments.length >= 1) {

src/compiler/checker.ts

Lines changed: 107 additions & 119 deletions
Large diffs are not rendered by default.

src/compiler/emitter.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,11 +1906,17 @@ namespace ts {
19061906
//
19071907

19081908
function emitTypePredicate(node: TypePredicateNode) {
1909+
if (node.assertsModifier) {
1910+
emit(node.assertsModifier);
1911+
writeSpace();
1912+
}
19091913
emit(node.parameterName);
1910-
writeSpace();
1911-
writeKeyword("is");
1912-
writeSpace();
1913-
emit(node.type);
1914+
if (node.type) {
1915+
writeSpace();
1916+
writeKeyword("is");
1917+
writeSpace();
1918+
emit(node.type);
1919+
}
19141920
}
19151921

19161922
function emitTypeReference(node: TypeReferenceNode) {

src/compiler/factory.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -667,17 +667,18 @@ namespace ts {
667667
return <KeywordTypeNode>createSynthesizedNode(kind);
668668
}
669669

670-
export function createTypePredicateNode(parameterName: Identifier | ThisTypeNode | string, type: TypeNode) {
670+
export function createTypePredicateNode(assertsModifier: AssertsToken | undefined, parameterName: Identifier | ThisTypeNode | string, type: TypeNode | undefined) {
671671
const node = createSynthesizedNode(SyntaxKind.TypePredicate) as TypePredicateNode;
672+
node.assertsModifier = assertsModifier;
672673
node.parameterName = asName(parameterName);
673674
node.type = type;
674675
return node;
675676
}
676677

677-
export function updateTypePredicateNode(node: TypePredicateNode, parameterName: Identifier | ThisTypeNode, type: TypeNode) {
678+
export function updateTypePredicateNode(node: TypePredicateNode, assertsModifier: AssertsToken | undefined, parameterName: Identifier | ThisTypeNode, type: TypeNode | undefined) {
678679
return node.parameterName !== parameterName
679680
|| node.type !== type
680-
? updateNode(createTypePredicateNode(parameterName, type), node)
681+
? updateNode(createTypePredicateNode(assertsModifier, parameterName, type), node)
681682
: node;
682683
}
683684

src/compiler/parser.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2992,6 +2992,8 @@ namespace ts {
29922992
return parseParenthesizedType();
29932993
case SyntaxKind.ImportKeyword:
29942994
return parseImportType();
2995+
case SyntaxKind.AssertsKeyword:
2996+
return lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine) ? parseAssertsTypePredicate() : parseTypeReference();
29952997
default:
29962998
return parseTypeReference();
29972999
}
@@ -3032,6 +3034,7 @@ namespace ts {
30323034
case SyntaxKind.DotDotDotToken:
30333035
case SyntaxKind.InferKeyword:
30343036
case SyntaxKind.ImportKeyword:
3037+
case SyntaxKind.AssertsKeyword:
30353038
return true;
30363039
case SyntaxKind.FunctionKeyword:
30373040
return !inStartOfParameter;
@@ -3225,6 +3228,16 @@ namespace ts {
32253228
}
32263229
}
32273230

3231+
function parseAssertsTypePredicate(): TypeNode {
3232+
const node = <TypePredicateNode>createNode(SyntaxKind.TypePredicate);
3233+
node.assertsModifier = parseExpectedToken(SyntaxKind.AssertsKeyword);
3234+
node.parameterName = parseIdentifier();
3235+
if (parseOptional(SyntaxKind.IsKeyword)) {
3236+
node.type = parseType();
3237+
}
3238+
return finishNode(node);
3239+
}
3240+
32283241
function parseType(): TypeNode {
32293242
// The rules about 'yield' only apply to actual code/expression contexts. They don't
32303243
// apply to 'type' contexts. So we disable these parameters here before moving on.

src/compiler/scanner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ namespace ts {
6565
abstract: SyntaxKind.AbstractKeyword,
6666
any: SyntaxKind.AnyKeyword,
6767
as: SyntaxKind.AsKeyword,
68+
asserts: SyntaxKind.AssertsKeyword,
6869
bigint: SyntaxKind.BigIntKeyword,
6970
boolean: SyntaxKind.BooleanKeyword,
7071
break: SyntaxKind.BreakKeyword,

src/compiler/types.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace ts {
3232
| SyntaxKind.AbstractKeyword
3333
| SyntaxKind.AnyKeyword
3434
| SyntaxKind.AsKeyword
35+
| SyntaxKind.AssertsKeyword
3536
| SyntaxKind.BigIntKeyword
3637
| SyntaxKind.BooleanKeyword
3738
| SyntaxKind.BreakKeyword
@@ -250,6 +251,7 @@ namespace ts {
250251
// Contextual keywords
251252
AbstractKeyword,
252253
AsKeyword,
254+
AssertsKeyword,
253255
AnyKeyword,
254256
AsyncKeyword,
255257
AwaitKeyword,
@@ -734,6 +736,7 @@ namespace ts {
734736
export type AwaitKeywordToken = Token<SyntaxKind.AwaitKeyword>;
735737
export type PlusToken = Token<SyntaxKind.PlusToken>;
736738
export type MinusToken = Token<SyntaxKind.MinusToken>;
739+
export type AssertsToken = Token<SyntaxKind.AssertsKeyword>;
737740

738741
export type Modifier
739742
= Token<SyntaxKind.AbstractKeyword>
@@ -1177,8 +1180,9 @@ namespace ts {
11771180
export interface TypePredicateNode extends TypeNode {
11781181
kind: SyntaxKind.TypePredicate;
11791182
parent: SignatureDeclaration | JSDocTypeExpression;
1183+
assertsModifier?: AssertsToken;
11801184
parameterName: Identifier | ThisTypeNode;
1181-
type: TypeNode;
1185+
type?: TypeNode;
11821186
}
11831187

11841188
export interface TypeQueryNode extends TypeNode {
@@ -3493,25 +3497,32 @@ namespace ts {
34933497

34943498
export const enum TypePredicateKind {
34953499
This,
3496-
Identifier
3500+
Identifier,
3501+
Assertion
34973502
}
34983503

3499-
export interface TypePredicateBase {
3500-
kind: TypePredicateKind;
3504+
export interface ThisTypePredicate {
3505+
kind: TypePredicateKind.This;
3506+
parameterName: undefined;
3507+
parameterIndex: undefined;
35013508
type: Type;
35023509
}
35033510

3504-
export interface ThisTypePredicate extends TypePredicateBase {
3505-
kind: TypePredicateKind.This;
3511+
export interface IdentifierTypePredicate {
3512+
kind: TypePredicateKind.Identifier;
3513+
parameterName: string;
3514+
parameterIndex: number;
3515+
type: Type;
35063516
}
35073517

3508-
export interface IdentifierTypePredicate extends TypePredicateBase {
3509-
kind: TypePredicateKind.Identifier;
3518+
export interface AssertionTypePredicate {
3519+
kind: TypePredicateKind.Assertion;
35103520
parameterName: string;
35113521
parameterIndex: number;
3522+
type: Type | undefined;
35123523
}
35133524

3514-
export type TypePredicate = IdentifierTypePredicate | ThisTypePredicate;
3525+
export type TypePredicate = ThisTypePredicate | IdentifierTypePredicate | AssertionTypePredicate;
35153526

35163527
/* @internal */
35173528
export type AnyImportSyntax = ImportDeclaration | ImportEqualsDeclaration;
@@ -3907,8 +3918,9 @@ namespace ts {
39073918
resolvedSignature?: Signature; // Cached signature of signature node or call expression
39083919
resolvedSymbol?: Symbol; // Cached name resolution result
39093920
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
3910-
maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate
3911-
isAssertCall?: boolean;
3921+
//maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate
3922+
//isAssertCall?: boolean;
3923+
resolvedTypePredicate?: TypePredicate; // Cached type predicate for call expression
39123924
enumMemberValue?: string | number; // Constant value of enum member
39133925
isVisible?: boolean; // Is this node visible
39143926
containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference

src/compiler/visitor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ namespace ts {
340340

341341
case SyntaxKind.TypePredicate:
342342
return updateTypePredicateNode(<TypePredicateNode>node,
343+
visitNode((<TypePredicateNode>node).assertsModifier, visitor),
343344
visitNode((<TypePredicateNode>node).parameterName, visitor),
344345
visitNode((<TypePredicateNode>node).type, visitor, isTypeNode));
345346

0 commit comments

Comments
 (0)