mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-11 01:34:55 -06:00
first pass at this type predicate members
This commit is contained in:
parent
d51dd84f6c
commit
b9f310d4f2
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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'.
|
||||
|
||||
130
tests/baselines/reference/typeGuardOfFormThisMember.js
Normal file
130
tests/baselines/reference/typeGuardOfFormThisMember.js
Normal 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;
|
||||
}
|
||||
}
|
||||
126
tests/baselines/reference/typeGuardOfFormThisMember.symbols
Normal file
126
tests/baselines/reference/typeGuardOfFormThisMember.symbols
Normal 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))
|
||||
}
|
||||
}
|
||||
136
tests/baselines/reference/typeGuardOfFormThisMember.types
Normal file
136
tests/baselines/reference/typeGuardOfFormThisMember.types
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user