first pass at this type predicate members

This commit is contained in:
Wesley Wigham 2015-12-03 18:21:36 -08:00
parent d51dd84f6c
commit b9f310d4f2
9 changed files with 611 additions and 93 deletions

View File

@ -1670,10 +1670,16 @@ namespace ts {
function writeType(type: Type, flags: TypeFormatFlags) {
// Write undefined/null type as any
if (type.flags & TypeFlags.Intrinsic) {
// Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving
writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && isTypeAny(type)
? "any"
: (<IntrinsicType>type).intrinsicName);
if (type.flags & TypeFlags.PredicateType) {
buildTypePredicateDisplay(writer, (type as PredicateType).predicate);
buildTypeDisplay((type as PredicateType).predicate.type, writer, enclosingDeclaration, flags, symbolStack);
}
else {
// Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving
writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && isTypeAny(type)
? "any"
: (<IntrinsicType>type).intrinsicName);
}
}
else if (type.flags & TypeFlags.ThisType) {
if (inObjectTypeLiteral) {
@ -2046,6 +2052,18 @@ namespace ts {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
function buildTypePredicateDisplay(writer: SymbolWriter, predicate: TypePredicate) {
if (isIdentifierTypePredicate(predicate)) {
writer.writeParameter(predicate.parameterName);
}
else {
writeKeyword(writer, SyntaxKind.ThisKeyword);
}
writeSpace(writer);
writeKeyword(writer, SyntaxKind.IsKeyword);
writeSpace(writer);
}
function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (flags & TypeFormatFlags.WriteArrowStyleSignature) {
writeSpace(writer);
@ -2059,15 +2077,7 @@ namespace ts {
let returnType: Type;
const predicate = signature.typePredicate;
if (predicate) {
if (isIdentifierTypePredicate(predicate)) {
writer.writeParameter(predicate.parameterName);
}
else {
writeKeyword(writer, SyntaxKind.ThisKeyword);
}
writeSpace(writer);
writeKeyword(writer, SyntaxKind.IsKeyword);
writeSpace(writer);
buildTypePredicateDisplay(writer, predicate);
returnType = predicate.type;
}
else {
@ -3850,6 +3860,24 @@ namespace ts {
return false;
}
function createTypePredicateFromTypePredicateNode(node: TypePredicateNode): IdentifierTypePredicate | ThisTypePredicate {
if (node.parameterName.kind === SyntaxKind.Identifier) {
const parameterName = node.parameterName as Identifier;
return {
kind: TypePredicateKind.Identifier,
parameterName: parameterName ? parameterName.text : undefined,
parameterIndex: parameterName ? getTypePredicateParameterIndex((node.parent as SignatureDeclaration).parameters, parameterName) : undefined,
type: getTypeFromTypeNode(node.type)
} as IdentifierTypePredicate;
}
else {
return {
kind: TypePredicateKind.This,
type: getTypeFromTypeNode(node.type)
} as ThisTypePredicate;
}
}
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
const links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
@ -3897,22 +3925,7 @@ namespace ts {
else if (declaration.type) {
returnType = getTypeFromTypeNode(declaration.type);
if (declaration.type.kind === SyntaxKind.TypePredicate) {
const typePredicateNode = declaration.type as TypePredicateNode;
if (typePredicateNode.parameterName.kind === SyntaxKind.Identifier) {
const parameterName = typePredicateNode.parameterName as Identifier;
typePredicate = {
kind: TypePredicateKind.Identifier,
parameterName: parameterName ? parameterName.text : undefined,
parameterIndex: parameterName ? getTypePredicateParameterIndex(declaration.parameters, parameterName) : undefined,
type: getTypeFromTypeNode(typePredicateNode.type)
};
}
else {
typePredicate = {
kind: TypePredicateKind.This,
type: getTypeFromTypeNode(typePredicateNode.type)
} as ThisTypePredicate;
}
typePredicate = createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode);
}
}
else {
@ -4588,6 +4601,26 @@ namespace ts {
return links.resolvedType;
}
function getPredicateType(node: TypePredicateNode): Type {
if (!(node.parent.kind === SyntaxKind.PropertyDeclaration || node.parent.kind === SyntaxKind.GetAccessor)) {
return booleanType;
}
else {
const type = createType(TypeFlags.Boolean | TypeFlags.PredicateType) as PredicateType;
type.symbol = getSymbolOfNode(node);
type.predicate = createTypePredicateFromTypePredicateNode(node) as ThisTypePredicate;
return type;
}
}
function getTypeFromPredicateTypeNode(node: TypePredicateNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getPredicateType(node);
}
return links.resolvedType;
}
function getTypeFromTypeNode(node: TypeNode): Type {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
@ -4609,7 +4642,7 @@ namespace ts {
case SyntaxKind.TypeReference:
return getTypeFromTypeReference(<TypeReferenceNode>node);
case SyntaxKind.TypePredicate:
return booleanType;
return getTypeFromPredicateTypeNode(<TypePredicateNode>node);
case SyntaxKind.ExpressionWithTypeArguments:
return getTypeFromTypeReference(<ExpressionWithTypeArguments>node);
case SyntaxKind.TypeQuery:
@ -4998,6 +5031,7 @@ namespace ts {
if (relation === assignableRelation) {
if (isTypeAny(source)) return Ternary.True;
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean && (source.flags & TypeFlags.PredicateType || target.flags & TypeFlags.PredicateType)) return Ternary.True;
}
if (source.flags & TypeFlags.FreshObjectLiteral) {
@ -6747,20 +6781,37 @@ namespace ts {
}
const predicate = signature.typePredicate;
if (isIdentifierTypePredicate(predicate)) {
if (expr.arguments[predicate.parameterIndex] &&
getSymbolAtTypePredicatePosition(expr.arguments[predicate.parameterIndex]) === symbol) {
const callExpression = expr as CallExpression;
if (callExpression.arguments[predicate.parameterIndex] &&
getSymbolAtTypePredicatePosition(callExpression.arguments[predicate.parameterIndex]) === symbol) {
return getNarrowedType(type, predicate.type, assumeTrue);
}
}
else {
const expression = skipParenthesizedNodes(expr.expression);
return narrowTypeByThisTypePredicate(type, predicate, expression, assumeTrue);
}
return type;
}
if (expression.kind === SyntaxKind.ElementAccessExpression || expression.kind === SyntaxKind.PropertyAccessExpression) {
const accessExpression = expression as ElementAccessExpression | PropertyAccessExpression;
const possibleIdentifier = skipParenthesizedNodes(accessExpression.expression);
if (possibleIdentifier.kind === SyntaxKind.Identifier && getSymbolAtTypePredicatePosition(possibleIdentifier) === symbol) {
return getNarrowedType(type, predicate.type, assumeTrue);
}
function narrowTypeByTypePredicateMember(type: Type, expr: ElementAccessExpression | PropertyAccessExpression, assumeTrue: boolean): Type {
if (type.flags & TypeFlags.Any) {
return type;
}
const memberType = getTypeOfExpression(expr);
if (!(memberType.flags & TypeFlags.PredicateType)) {
return type;
}
return narrowTypeByThisTypePredicate(type, (memberType as PredicateType).predicate, expr, assumeTrue);
}
function narrowTypeByThisTypePredicate(type: Type, predicate: ThisTypePredicate, expression: Expression, assumeTrue: boolean): Type {
if (expression.kind === SyntaxKind.ElementAccessExpression || expression.kind === SyntaxKind.PropertyAccessExpression) {
const accessExpression = expression as ElementAccessExpression | PropertyAccessExpression;
const possibleIdentifier = skipParenthesizedNodes(accessExpression.expression);
if (possibleIdentifier.kind === SyntaxKind.Identifier && getSymbolAtTypePredicatePosition(possibleIdentifier) === symbol) {
return getNarrowedType(type, predicate.type, assumeTrue);
}
}
return type;
@ -6811,6 +6862,9 @@ namespace ts {
return narrowType(type, (<PrefixUnaryExpression>expr).operand, !assumeTrue);
}
break;
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.PropertyAccessExpression:
return narrowTypeByTypePredicateMember(type, expr as (ElementAccessExpression | PropertyAccessExpression), assumeTrue);
}
return type;
}
@ -11018,6 +11072,18 @@ namespace ts {
return false;
}
function isInLegalThisTypePredicatePosition(node: Node): boolean {
if (isInLegalTypePredicatePosition(node)) {
return true;
}
switch (node.parent.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.GetAccessor:
return node === (<SignatureDeclaration>node.parent).type;
}
return false;
}
function checkSignatureDeclaration(node: SignatureDeclaration) {
// Grammar checking
if (node.kind === SyntaxKind.IndexSignature) {
@ -11038,63 +11104,67 @@ namespace ts {
if (node.type.kind === SyntaxKind.TypePredicate) {
const typePredicate = getSignatureFromDeclaration(node).typePredicate;
const typePredicateNode = <TypePredicateNode>node.type;
if (isInLegalTypePredicatePosition(typePredicateNode)) {
if (isIdentifierTypePredicate(typePredicate)) {
if (typePredicate.parameterIndex >= 0) {
if (node.parameters[typePredicate.parameterIndex].dotDotDotToken) {
error(typePredicateNode.parameterName,
Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter);
}
else {
checkTypeAssignableTo(typePredicate.type,
getTypeOfNode(node.parameters[typePredicate.parameterIndex]),
typePredicateNode.type);
}
if (isIdentifierTypePredicate(typePredicate)) {
if (!isInLegalTypePredicatePosition(typePredicateNode)) {
error(typePredicateNode,
Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods);
return;
}
if (typePredicate.parameterIndex >= 0) {
if (node.parameters[typePredicate.parameterIndex].dotDotDotToken) {
error(typePredicateNode.parameterName,
Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter);
}
else if (typePredicateNode.parameterName) {
let hasReportedError = false;
for (var param of node.parameters) {
if (hasReportedError) {
break;
}
if (param.name.kind === SyntaxKind.ObjectBindingPattern ||
param.name.kind === SyntaxKind.ArrayBindingPattern) {
(function checkBindingPattern(pattern: BindingPattern) {
for (const element of pattern.elements) {
if (element.name.kind === SyntaxKind.Identifier &&
(<Identifier>element.name).text === typePredicate.parameterName) {
error(typePredicateNode.parameterName,
Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern,
typePredicate.parameterName);
hasReportedError = true;
break;
}
else if (element.name.kind === SyntaxKind.ArrayBindingPattern ||
element.name.kind === SyntaxKind.ObjectBindingPattern) {
checkBindingPattern(<BindingPattern>element.name);
}
}
})(<BindingPattern>param.name);
}
}
if (!hasReportedError) {
error(typePredicateNode.parameterName,
Diagnostics.Cannot_find_parameter_0,
typePredicate.parameterName);
}
else {
checkTypeAssignableTo(typePredicate.type,
getTypeOfNode(node.parameters[typePredicate.parameterIndex]),
typePredicateNode.type);
}
}
else {
// Reuse this type diagnostics on the this type node to determine if a this type predicate is valid
getTypeFromThisTypeNode(typePredicateNode.parameterName as ThisTypeNode);
else if (typePredicateNode.parameterName) {
let hasReportedError = false;
for (var param of node.parameters) {
if (hasReportedError) {
break;
}
if (param.name.kind === SyntaxKind.ObjectBindingPattern ||
param.name.kind === SyntaxKind.ArrayBindingPattern) {
(function checkBindingPattern(pattern: BindingPattern) {
for (const element of pattern.elements) {
if (element.name.kind === SyntaxKind.Identifier &&
(<Identifier>element.name).text === typePredicate.parameterName) {
error(typePredicateNode.parameterName,
Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern,
typePredicate.parameterName);
hasReportedError = true;
break;
}
else if (element.name.kind === SyntaxKind.ArrayBindingPattern ||
element.name.kind === SyntaxKind.ObjectBindingPattern) {
checkBindingPattern(<BindingPattern>element.name);
}
}
})(<BindingPattern>param.name);
}
}
if (!hasReportedError) {
error(typePredicateNode.parameterName,
Diagnostics.Cannot_find_parameter_0,
typePredicate.parameterName);
}
}
}
else {
error(typePredicateNode,
Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods);
if (!isInLegalThisTypePredicatePosition(typePredicateNode)) {
error(typePredicateNode,
Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods);
return;
}
// Reuse this type diagnostics on the this type node to determine if a this type predicate is valid
getTypeFromThisTypeNode(typePredicateNode.parameterName as ThisTypeNode);
}
}
else {
@ -14222,9 +14292,12 @@ namespace ts {
}
function checkTypePredicate(node: TypePredicateNode) {
if (!isInLegalTypePredicatePosition(node)) {
if (node.parameterName.kind === SyntaxKind.Identifier && !isInLegalTypePredicatePosition(node)) {
error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods);
}
else if (node.parameterName.kind === SyntaxKind.ThisType && (!isInLegalThisTypePredicatePosition(node) || getTypeFromThisTypeNode(node.parameterName as ThisTypeNode) === unknownType)) {
error(node, Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods);
}
}
function checkSourceElement(node: Node): void {

View File

@ -1636,6 +1636,10 @@
"category": "Error",
"code": 2518
},
"A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions for functions and methods": {
"category": "Error",
"code": 2519
},
"Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": {
"category": "Error",
"code": 2520

View File

@ -2108,6 +2108,7 @@ namespace ts {
ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6
ThisType = 0x02000000, // This type
ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties
PredicateType = 0x08000000,
/* @internal */
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
@ -2140,6 +2141,11 @@ namespace ts {
intrinsicName: string; // Name of intrinsic type
}
// Predicate types (TypeFlags.Predicate)
export interface PredicateType extends Type {
predicate: ThisTypePredicate;
}
// String literal types (TypeFlags.StringLiteral)
export interface StringLiteralType extends Type {
text: string; // Text of string literal

View File

@ -693,7 +693,7 @@ namespace ts {
}
export function isIdentifierTypePredicate(predicate: TypePredicate): predicate is IdentifierTypePredicate {
return predicate.kind === TypePredicateKind.Identifier;
return predicate && predicate.kind === TypePredicateKind.Identifier;
}
export function getContainingFunction(node: Node): FunctionLikeDeclaration {

View File

@ -25,7 +25,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(91,1):
tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,9): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,16): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,20): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(104,25): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(104,25): error TS2519: A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions for functions and methods
tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(105,16): error TS2322: Type 'boolean' is not assignable to type 'D'.
Property 'm1' is missing in type 'Boolean'.
tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(105,16): error TS2409: Return type of constructor signature must be assignable to the instance type of the class
@ -194,7 +194,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39
class D {
constructor(p1: A): p1 is C {
~~~~~~~
!!! error TS1228: A type predicate is only allowed in return type position for functions and methods.
!!! error TS2519: A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions for functions and methods
return true;
~~~~
!!! error TS2322: Type 'boolean' is not assignable to type 'D'.

View File

@ -0,0 +1,130 @@
//// [typeGuardOfFormThisMember.ts]
// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision
namespace Test {
export class FileSystemObject {
get isFile(): this is File {
return this instanceof File;
}
set isFile(param) {
// noop
}
get isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
constructor(public path: string) {}
}
export class File extends FileSystemObject {
constructor(path: string, public content: string) { super(path); }
}
export class Directory extends FileSystemObject {
children: FileSystemObject[];
}
export interface Networked {
host: string;
}
let file: FileSystemObject = new File("foo/bar.txt", "foo");
file.isNetworked = false;
file.isNetworked = file.isDirectory;
file.isFile = file.isNetworked;
let x = file.isFile;
if (file.isFile) {
file.content;
}
else if (file.isDirectory) {
file.children;
}
else if (file.isNetworked) {
file.host;
}
}
//// [typeGuardOfFormThisMember.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision
var Test;
(function (Test) {
var FileSystemObject = (function () {
function FileSystemObject(path) {
this.path = path;
}
Object.defineProperty(FileSystemObject.prototype, "isFile", {
get: function () {
return this instanceof File;
},
set: function (param) {
// noop
},
enumerable: true,
configurable: true
});
Object.defineProperty(FileSystemObject.prototype, "isDirectory", {
get: function () {
return this instanceof Directory;
},
enumerable: true,
configurable: true
});
return FileSystemObject;
})();
Test.FileSystemObject = FileSystemObject;
var File = (function (_super) {
__extends(File, _super);
function File(path, content) {
_super.call(this, path);
this.content = content;
}
return File;
})(FileSystemObject);
Test.File = File;
var Directory = (function (_super) {
__extends(Directory, _super);
function Directory() {
_super.apply(this, arguments);
}
return Directory;
})(FileSystemObject);
Test.Directory = Directory;
var file = new File("foo/bar.txt", "foo");
file.isNetworked = false;
file.isNetworked = file.isDirectory;
file.isFile = file.isNetworked;
var x = file.isFile;
if (file.isFile) {
file.content;
}
else if (file.isDirectory) {
file.children;
}
else if (file.isNetworked) {
file.host;
}
})(Test || (Test = {}));
//// [typeGuardOfFormThisMember.d.ts]
declare namespace Test {
class FileSystemObject {
path: string;
isFile: this is File;
isDirectory: this is Directory;
isNetworked: this is (Networked & this);
constructor(path: string);
}
class File extends FileSystemObject {
content: string;
constructor(path: string, content: string);
}
class Directory extends FileSystemObject {
children: FileSystemObject[];
}
interface Networked {
host: string;
}
}

View File

@ -0,0 +1,126 @@
=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts ===
// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision
namespace Test {
>Test : Symbol(Test, Decl(typeGuardOfFormThisMember.ts, 0, 0))
export class FileSystemObject {
>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
get isFile(): this is File {
>isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2))
return this instanceof File;
>this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2))
}
set isFile(param) {
>isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
>param : Symbol(param, Decl(typeGuardOfFormThisMember.ts, 6, 13))
// noop
}
get isDirectory(): this is Directory {
>isDirectory : Symbol(isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3))
>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2))
return this instanceof Directory;
>this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2))
}
isNetworked: this is (Networked & this);
>isNetworked : Symbol(isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 21, 2))
constructor(public path: string) {}
>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 13, 14))
}
export class File extends FileSystemObject {
>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2))
>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
constructor(path: string, public content: string) { super(path); }
>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 17, 14))
>content : Symbol(content, Decl(typeGuardOfFormThisMember.ts, 17, 27))
>super : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 17, 14))
}
export class Directory extends FileSystemObject {
>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2))
>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
children: FileSystemObject[];
>children : Symbol(children, Decl(typeGuardOfFormThisMember.ts, 19, 50))
>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
}
export interface Networked {
>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 21, 2))
host: string;
>host : Symbol(host, Decl(typeGuardOfFormThisMember.ts, 22, 29))
}
let file: FileSystemObject = new File("foo/bar.txt", "foo");
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2))
file.isNetworked = false;
>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
file.isNetworked = file.isDirectory;
>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
>file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3))
file.isFile = file.isNetworked;
>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
let x = file.isFile;
>x : Symbol(x, Decl(typeGuardOfFormThisMember.ts, 30, 4))
>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
if (file.isFile) {
>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3))
file.content;
>file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27))
}
else if (file.isDirectory) {
>file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3))
file.children;
>file.children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 19, 50))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 19, 50))
}
else if (file.isNetworked) {
>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3))
file.host;
>file.host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29))
>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4))
>host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29))
}
}

View File

@ -0,0 +1,136 @@
=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts ===
// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision
namespace Test {
>Test : typeof Test
export class FileSystemObject {
>FileSystemObject : FileSystemObject
get isFile(): this is File {
>isFile : this is File
>File : File
return this instanceof File;
>this instanceof File : boolean
>this : this
>File : typeof File
}
set isFile(param) {
>isFile : this is File
>param : this is File
// noop
}
get isDirectory(): this is Directory {
>isDirectory : this is Directory
>Directory : Directory
return this instanceof Directory;
>this instanceof Directory : boolean
>this : this
>Directory : typeof Directory
}
isNetworked: this is (Networked & this);
>isNetworked : this is Networked & this
>Networked : Networked
constructor(public path: string) {}
>path : string
}
export class File extends FileSystemObject {
>File : File
>FileSystemObject : FileSystemObject
constructor(path: string, public content: string) { super(path); }
>path : string
>content : string
>super(path) : void
>super : typeof FileSystemObject
>path : string
}
export class Directory extends FileSystemObject {
>Directory : Directory
>FileSystemObject : FileSystemObject
children: FileSystemObject[];
>children : FileSystemObject[]
>FileSystemObject : FileSystemObject
}
export interface Networked {
>Networked : Networked
host: string;
>host : string
}
let file: FileSystemObject = new File("foo/bar.txt", "foo");
>file : FileSystemObject
>FileSystemObject : FileSystemObject
>new File("foo/bar.txt", "foo") : File
>File : typeof File
>"foo/bar.txt" : string
>"foo" : string
file.isNetworked = false;
>file.isNetworked = false : boolean
>file.isNetworked : this is Networked & this
>file : FileSystemObject
>isNetworked : this is Networked & this
>false : boolean
file.isNetworked = file.isDirectory;
>file.isNetworked = file.isDirectory : this is Directory
>file.isNetworked : this is Networked & this
>file : FileSystemObject
>isNetworked : this is Networked & this
>file.isDirectory : this is Directory
>file : FileSystemObject
>isDirectory : this is Directory
file.isFile = file.isNetworked;
>file.isFile = file.isNetworked : this is Networked & this
>file.isFile : this is File
>file : FileSystemObject
>isFile : this is File
>file.isNetworked : this is Networked & this
>file : FileSystemObject
>isNetworked : this is Networked & this
let x = file.isFile;
>x : this is File
>file.isFile : this is File
>file : FileSystemObject
>isFile : this is File
if (file.isFile) {
>file.isFile : this is File
>file : FileSystemObject
>isFile : this is File
file.content;
>file.content : string
>file : File
>content : string
}
else if (file.isDirectory) {
>file.isDirectory : this is Directory
>file : FileSystemObject
>isDirectory : this is Directory
file.children;
>file.children : FileSystemObject[]
>file : Directory
>children : FileSystemObject[]
}
else if (file.isNetworked) {
>file.isNetworked : this is Networked & this
>file : FileSystemObject
>isNetworked : this is Networked & this
file.host;
>file.host : string
>file : Networked & this
>host : string
}
}

View File

@ -0,0 +1,43 @@
// @target: es5
// @declaration: true
// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision
namespace Test {
export class FileSystemObject {
get isFile(): this is File {
return this instanceof File;
}
set isFile(param) {
// noop
}
get isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
constructor(public path: string) {}
}
export class File extends FileSystemObject {
constructor(path: string, public content: string) { super(path); }
}
export class Directory extends FileSystemObject {
children: FileSystemObject[];
}
export interface Networked {
host: string;
}
let file: FileSystemObject = new File("foo/bar.txt", "foo");
file.isNetworked = false;
file.isNetworked = file.isDirectory;
file.isFile = file.isNetworked;
let x = file.isFile;
if (file.isFile) {
file.content;
}
else if (file.isDirectory) {
file.children;
}
else if (file.isNetworked) {
file.host;
}
}