Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix type predicate check circularity #12210

Merged
merged 2 commits into from
Nov 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8981,6 +8981,28 @@ namespace ts {
return isLengthPushOrUnshift || isElementAssignment;
}

function maybeTypePredicateCall(node: CallExpression) {
const links = getNodeLinks(node);
if (links.maybeTypePredicate === undefined) {
links.maybeTypePredicate = getMaybeTypePredicate(node);
}
return links.maybeTypePredicate;
}

function getMaybeTypePredicate(node: CallExpression) {
if (node.expression.kind !== SyntaxKind.SuperKeyword) {
const funcType = checkNonNullExpression(node.expression);
if (funcType !== silentNeverType) {
const apparentType = getApparentType(funcType);
if (apparentType !== unknownType) {
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
return !!forEach(callSignatures, sig => sig.typePredicate);
}
}
}
return false;
}

function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) {
let key: string;
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
Expand Down Expand Up @@ -9495,7 +9517,7 @@ namespace ts {
}

function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
if (!hasMatchingArgument(callExpression, reference)) {
if (!hasMatchingArgument(callExpression, reference) || !maybeTypePredicateCall(callExpression)) {
return type;
}
const signature = getResolvedSignature(callExpression);
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2668,6 +2668,7 @@ namespace ts {
resolvedSignature?: Signature; // Cached signature of signature node or call expression
resolvedSymbol?: Symbol; // Cached name resolution result
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate
enumMemberValue?: number; // Constant value of enum member
isVisible?: boolean; // Is this node visible
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
Expand Down
38 changes: 38 additions & 0 deletions tests/baselines/reference/typePredicateInLoop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//// [typePredicateInLoop.ts]
// Repro from #12101

interface Type {
type: number;
}

interface TypeExt extends Type {
arr: Type[];
}

const guard = (arg: Type): arg is TypeExt => arg.type === 1;
const otherFunc = (arg1: Type, arg2: TypeExt): void => {};

export function y(arg: Type): void {
if (guard(arg)) {
for (const ITEM of arg.arr) {
if (otherFunc(ITEM, arg)) {
}
}
}
}

//// [typePredicateInLoop.js]
// Repro from #12101
"use strict";
var guard = function (arg) { return arg.type === 1; };
var otherFunc = function (arg1, arg2) { };
function y(arg) {
if (guard(arg)) {
for (var _i = 0, _a = arg.arr; _i < _a.length; _i++) {
var ITEM = _a[_i];
if (otherFunc(ITEM, arg)) {
}
}
}
}
exports.y = y;
59 changes: 59 additions & 0 deletions tests/baselines/reference/typePredicateInLoop.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
=== tests/cases/compiler/typePredicateInLoop.ts ===
// Repro from #12101

interface Type {
>Type : Symbol(Type, Decl(typePredicateInLoop.ts, 0, 0))

type: number;
>type : Symbol(Type.type, Decl(typePredicateInLoop.ts, 2, 16))
}

interface TypeExt extends Type {
>TypeExt : Symbol(TypeExt, Decl(typePredicateInLoop.ts, 4, 1))
>Type : Symbol(Type, Decl(typePredicateInLoop.ts, 0, 0))

arr: Type[];
>arr : Symbol(TypeExt.arr, Decl(typePredicateInLoop.ts, 6, 32))
>Type : Symbol(Type, Decl(typePredicateInLoop.ts, 0, 0))
}

const guard = (arg: Type): arg is TypeExt => arg.type === 1;
>guard : Symbol(guard, Decl(typePredicateInLoop.ts, 10, 5))
>arg : Symbol(arg, Decl(typePredicateInLoop.ts, 10, 15))
>Type : Symbol(Type, Decl(typePredicateInLoop.ts, 0, 0))
>arg : Symbol(arg, Decl(typePredicateInLoop.ts, 10, 15))
>TypeExt : Symbol(TypeExt, Decl(typePredicateInLoop.ts, 4, 1))
>arg.type : Symbol(Type.type, Decl(typePredicateInLoop.ts, 2, 16))
>arg : Symbol(arg, Decl(typePredicateInLoop.ts, 10, 15))
>type : Symbol(Type.type, Decl(typePredicateInLoop.ts, 2, 16))

const otherFunc = (arg1: Type, arg2: TypeExt): void => {};
>otherFunc : Symbol(otherFunc, Decl(typePredicateInLoop.ts, 11, 5))
>arg1 : Symbol(arg1, Decl(typePredicateInLoop.ts, 11, 19))
>Type : Symbol(Type, Decl(typePredicateInLoop.ts, 0, 0))
>arg2 : Symbol(arg2, Decl(typePredicateInLoop.ts, 11, 30))
>TypeExt : Symbol(TypeExt, Decl(typePredicateInLoop.ts, 4, 1))

export function y(arg: Type): void {
>y : Symbol(y, Decl(typePredicateInLoop.ts, 11, 58))
>arg : Symbol(arg, Decl(typePredicateInLoop.ts, 13, 18))
>Type : Symbol(Type, Decl(typePredicateInLoop.ts, 0, 0))

if (guard(arg)) {
>guard : Symbol(guard, Decl(typePredicateInLoop.ts, 10, 5))
>arg : Symbol(arg, Decl(typePredicateInLoop.ts, 13, 18))

for (const ITEM of arg.arr) {
>ITEM : Symbol(ITEM, Decl(typePredicateInLoop.ts, 15, 14))
>arg.arr : Symbol(TypeExt.arr, Decl(typePredicateInLoop.ts, 6, 32))
>arg : Symbol(arg, Decl(typePredicateInLoop.ts, 13, 18))
>arr : Symbol(TypeExt.arr, Decl(typePredicateInLoop.ts, 6, 32))

if (otherFunc(ITEM, arg)) {
>otherFunc : Symbol(otherFunc, Decl(typePredicateInLoop.ts, 11, 5))
>ITEM : Symbol(ITEM, Decl(typePredicateInLoop.ts, 15, 14))
>arg : Symbol(arg, Decl(typePredicateInLoop.ts, 13, 18))
}
}
}
}
65 changes: 65 additions & 0 deletions tests/baselines/reference/typePredicateInLoop.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
=== tests/cases/compiler/typePredicateInLoop.ts ===
// Repro from #12101

interface Type {
>Type : Type

type: number;
>type : number
}

interface TypeExt extends Type {
>TypeExt : TypeExt
>Type : Type

arr: Type[];
>arr : Type[]
>Type : Type
}

const guard = (arg: Type): arg is TypeExt => arg.type === 1;
>guard : (arg: Type) => arg is TypeExt
>(arg: Type): arg is TypeExt => arg.type === 1 : (arg: Type) => arg is TypeExt
>arg : Type
>Type : Type
>arg : any
>TypeExt : TypeExt
>arg.type === 1 : boolean
>arg.type : number
>arg : Type
>type : number
>1 : 1

const otherFunc = (arg1: Type, arg2: TypeExt): void => {};
>otherFunc : (arg1: Type, arg2: TypeExt) => void
>(arg1: Type, arg2: TypeExt): void => {} : (arg1: Type, arg2: TypeExt) => void
>arg1 : Type
>Type : Type
>arg2 : TypeExt
>TypeExt : TypeExt

export function y(arg: Type): void {
>y : (arg: Type) => void
>arg : Type
>Type : Type

if (guard(arg)) {
>guard(arg) : boolean
>guard : (arg: Type) => arg is TypeExt
>arg : Type

for (const ITEM of arg.arr) {
>ITEM : Type
>arg.arr : Type[]
>arg : TypeExt
>arr : Type[]

if (otherFunc(ITEM, arg)) {
>otherFunc(ITEM, arg) : void
>otherFunc : (arg1: Type, arg2: TypeExt) => void
>ITEM : Type
>arg : TypeExt
}
}
}
}
21 changes: 21 additions & 0 deletions tests/cases/compiler/typePredicateInLoop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Repro from #12101

interface Type {
type: number;
}

interface TypeExt extends Type {
arr: Type[];
}

const guard = (arg: Type): arg is TypeExt => arg.type === 1;
const otherFunc = (arg1: Type, arg2: TypeExt): void => {};

export function y(arg: Type): void {
if (guard(arg)) {
for (const ITEM of arg.arr) {
if (otherFunc(ITEM, arg)) {
}
}
}
}