Adds type guard methods
This commit is contained in:
parent
f8e2b99b6c
commit
b7d1df68fb
7 changed files with 350 additions and 23 deletions
|
@ -5646,6 +5646,24 @@ module ts {
|
|||
return type;
|
||||
}
|
||||
|
||||
function shouldNarrowTypeByTypePredicate(signature: Signature, expr: CallExpression): boolean {
|
||||
if (!signature.typePredicate) {
|
||||
return false;
|
||||
}
|
||||
if (expr.arguments &&
|
||||
expr.arguments[signature.typePredicate.parameterIndex] &&
|
||||
getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) {
|
||||
|
||||
return true;
|
||||
}
|
||||
if (expr.expression.kind === SyntaxKind.PropertyAccessExpression &&
|
||||
getSymbolAtLocation((<PropertyAccessExpression>expr.expression).expression) === symbol) {
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function narrowTypeByTypePredicate(type: Type, expr: CallExpression, assumeTrue: boolean): Type {
|
||||
if (type.flags & TypeFlags.Any) {
|
||||
return type;
|
||||
|
@ -5657,16 +5675,12 @@ module ts {
|
|||
}
|
||||
return type;
|
||||
}
|
||||
if (signature.typePredicate) {
|
||||
if (expr.arguments && expr.arguments[signature.typePredicate.parameterIndex]) {
|
||||
if (getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) {
|
||||
if (isTypeSubtypeOf(signature.typePredicate.type, type)) {
|
||||
return signature.typePredicate.type;
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return getUnionType(filter((<UnionType>type).types, t => isTypeSubtypeOf(t, signature.typePredicate.type)));
|
||||
}
|
||||
}
|
||||
if (shouldNarrowTypeByTypePredicate(signature, expr)) {
|
||||
if (isTypeSubtypeOf(signature.typePredicate.type, type)) {
|
||||
return signature.typePredicate.type;
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return getUnionType(filter((<UnionType>type).types, t => isTypeSubtypeOf(t, signature.typePredicate.type)));
|
||||
}
|
||||
}
|
||||
return type;
|
||||
|
@ -8622,8 +8636,7 @@ module ts {
|
|||
links.typeFromTypePredicate = getTypeFromTypeNode(node.typePredicate.type);
|
||||
}
|
||||
if (links.typePredicateParameterIndex >= 0) {
|
||||
checkTypeAssignableTo(
|
||||
links.typeFromTypePredicate,
|
||||
checkTypeAssignableTo(links.typeFromTypePredicate,
|
||||
getTypeAtLocation(node.parameters[links.typePredicateParameterIndex]),
|
||||
node.typePredicate.type);
|
||||
}
|
||||
|
@ -8632,6 +8645,17 @@ module ts {
|
|||
Diagnostics.Cannot_find_parameter_0,
|
||||
node.typePredicate.parameterName.text);
|
||||
}
|
||||
else {
|
||||
let typeOfClass = getTypeAtLocation(node.parent);
|
||||
if (!isTypeSubtypeOf(links.typeFromTypePredicate, typeOfClass) &&
|
||||
!isTypeSubtypeOf(typeOfClass, links.typeFromTypePredicate)) {
|
||||
|
||||
error(node.typePredicate,
|
||||
Diagnostics.Type_0_and_type_1_are_disjoint_types,
|
||||
typeToString(links.typeFromTypePredicate),
|
||||
typeToString(typeOfClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (produceDiagnostics) {
|
||||
|
|
|
@ -184,6 +184,7 @@ module ts {
|
|||
Cannot_find_parameter_0: { code: 1226, category: DiagnosticCategory.Error, key: "Cannot find parameter '{0}'." },
|
||||
Type_guard_annotation_0_is_not_assignable_to_1: { code: 1227, category: DiagnosticCategory.Error, key: "Type-guard annotation '{0}' is not assignable to '{1}'." },
|
||||
Parameter_index_from_0_does_not_match_the_parameter_index_from_1: { code: 1228, category: DiagnosticCategory.Error, key: "Parameter index from '{0}' does not match the parameter index from '{1}'." },
|
||||
Type_0_and_type_1_are_disjoint_types: { code: 1229, category: DiagnosticCategory.Error, key: "Type '{0}' and type '{1}' are disjoint types." },
|
||||
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
|
||||
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
|
||||
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },
|
||||
|
|
|
@ -723,6 +723,10 @@
|
|||
"category": "Error",
|
||||
"code": 1228
|
||||
},
|
||||
"Type '{0}' and type '{1}' are disjoint types.": {
|
||||
"category": "Error",
|
||||
"code": 1229
|
||||
},
|
||||
|
||||
|
||||
"Duplicate identifier '{0}'.": {
|
||||
|
|
|
@ -2014,6 +2014,34 @@ module ts {
|
|||
return parseInitializer(/*inParameter*/ true);
|
||||
}
|
||||
|
||||
function parseTypePredicate(signature: SignatureDeclaration) {
|
||||
let node = <TypePredicateNode>createNode(SyntaxKind.TypePredicate);
|
||||
if (token !== SyntaxKind.ThisKeyword) {
|
||||
node.pos = signature.type.pos;
|
||||
node.parameterName = <Identifier>(<TypeReferenceNode>signature.type).typeName;
|
||||
signature.type = undefined;
|
||||
}
|
||||
else {
|
||||
// Swallow `this`
|
||||
nextToken();
|
||||
}
|
||||
|
||||
// Swallow `is`
|
||||
nextToken();
|
||||
|
||||
node.type = parseType();
|
||||
signature.typePredicate = finishNode(node);
|
||||
}
|
||||
|
||||
function parseTypePredicateOrReturnType(signature: SignatureDeclaration) {
|
||||
if (token === SyntaxKind.ThisKeyword) {
|
||||
parseTypePredicate(signature);
|
||||
}
|
||||
else {
|
||||
signature.type = parseType();
|
||||
}
|
||||
}
|
||||
|
||||
function fillSignature(
|
||||
returnToken: SyntaxKind,
|
||||
yieldAndGeneratorParameterContext: boolean,
|
||||
|
@ -2025,21 +2053,13 @@ module ts {
|
|||
|
||||
if (returnTokenRequired) {
|
||||
parseExpected(returnToken);
|
||||
signature.type = parseType();
|
||||
parseTypePredicateOrReturnType(signature);
|
||||
}
|
||||
else if (parseOptional(returnToken)) {
|
||||
signature.type = parseType();
|
||||
parseTypePredicateOrReturnType(signature);
|
||||
}
|
||||
if (token === SyntaxKind.IsKeyword) {
|
||||
let node = <TypePredicateNode>createNode(SyntaxKind.TypePredicate);
|
||||
node.pos = signature.type.pos;
|
||||
node.parameterName = <Identifier>(<TypeReferenceNode>signature.type).typeName;
|
||||
|
||||
nextToken();
|
||||
|
||||
node.type = parseType();
|
||||
signature.type = undefined;
|
||||
signature.typePredicate = finishNode(node);
|
||||
parseTypePredicate(signature);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
73
tests/baselines/reference/typeGuardMethods.errors.txt
Normal file
73
tests/baselines/reference/typeGuardMethods.errors.txt
Normal file
|
@ -0,0 +1,73 @@
|
|||
tests/cases/conformance/expressions/typeGuards/typeGuardMethods.ts(14,12): error TS1229: Type 'A' and type 'B' are disjoint types.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardMethods.ts(17,12): error TS1229: Type 'C' and type 'B' are disjoint types.
|
||||
|
||||
|
||||
==== tests/cases/conformance/expressions/typeGuards/typeGuardMethods.ts (2 errors) ====
|
||||
|
||||
class A {
|
||||
propA: number;
|
||||
isA(): this is A {
|
||||
return true;
|
||||
}
|
||||
isC(): this is C {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
propB: number;
|
||||
isA(): this is A {
|
||||
~~~~~~~~~
|
||||
!!! error TS1229: Type 'A' and type 'B' are disjoint types.
|
||||
return false;
|
||||
}
|
||||
isC(): this is C {
|
||||
~~~~~~~~~
|
||||
!!! error TS1229: Type 'C' and type 'B' are disjoint types.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class C extends A {
|
||||
propC: number;
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isC(): this is C {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isString(x: any): x is string { // with parameter declaration
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var a: A;
|
||||
|
||||
// Basic.
|
||||
if (a.isC()) {
|
||||
a.propC;
|
||||
}
|
||||
|
||||
// Sub type.
|
||||
var subType: C;
|
||||
if(subType.isA()) {
|
||||
subType.propC;
|
||||
}
|
||||
|
||||
// Union type.
|
||||
var union: A | B;
|
||||
if(union.isA()) {
|
||||
union.propA;
|
||||
}
|
||||
|
||||
var b: any;
|
||||
var d = new D;
|
||||
if(d.isString(b)) {
|
||||
b.length;
|
||||
}
|
141
tests/baselines/reference/typeGuardMethods.js
Normal file
141
tests/baselines/reference/typeGuardMethods.js
Normal file
|
@ -0,0 +1,141 @@
|
|||
//// [typeGuardMethods.ts]
|
||||
|
||||
class A {
|
||||
propA: number;
|
||||
isA(): this is A {
|
||||
return true;
|
||||
}
|
||||
isC(): this is C {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
propB: number;
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isC(): this is C {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class C extends A {
|
||||
propC: number;
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isC(): this is C {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isString(x: any): x is string { // with parameter declaration
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var a: A;
|
||||
|
||||
// Basic.
|
||||
if (a.isC()) {
|
||||
a.propC;
|
||||
}
|
||||
|
||||
// Sub type.
|
||||
var subType: C;
|
||||
if(subType.isA()) {
|
||||
subType.propC;
|
||||
}
|
||||
|
||||
// Union type.
|
||||
var union: A | B;
|
||||
if(union.isA()) {
|
||||
union.propA;
|
||||
}
|
||||
|
||||
var b: any;
|
||||
var d = new D;
|
||||
if(d.isString(b)) {
|
||||
b.length;
|
||||
}
|
||||
|
||||
//// [typeGuardMethods.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; }
|
||||
__.prototype = b.prototype;
|
||||
d.prototype = new __();
|
||||
};
|
||||
var A = (function () {
|
||||
function A() {
|
||||
}
|
||||
A.prototype.isA = function () {
|
||||
return true;
|
||||
};
|
||||
A.prototype.isC = function () {
|
||||
return false;
|
||||
};
|
||||
return A;
|
||||
})();
|
||||
var B = (function () {
|
||||
function B() {
|
||||
}
|
||||
B.prototype.isA = function () {
|
||||
return false;
|
||||
};
|
||||
B.prototype.isC = function () {
|
||||
return false;
|
||||
};
|
||||
return B;
|
||||
})();
|
||||
var C = (function (_super) {
|
||||
__extends(C, _super);
|
||||
function C() {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
C.prototype.isA = function () {
|
||||
return false;
|
||||
};
|
||||
C.prototype.isC = function () {
|
||||
return true;
|
||||
};
|
||||
return C;
|
||||
})(A);
|
||||
var D = (function (_super) {
|
||||
__extends(D, _super);
|
||||
function D() {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
D.prototype.isA = function () {
|
||||
return false;
|
||||
};
|
||||
D.prototype.isString = function (x) {
|
||||
return true;
|
||||
};
|
||||
return D;
|
||||
})(C);
|
||||
var a;
|
||||
// Basic.
|
||||
if (a.isC()) {
|
||||
a.propC;
|
||||
}
|
||||
// Sub type.
|
||||
var subType;
|
||||
if (subType.isA()) {
|
||||
subType.propC;
|
||||
}
|
||||
// Union type.
|
||||
var union;
|
||||
if (union.isA()) {
|
||||
union.propA;
|
||||
}
|
||||
var b;
|
||||
var d = new D;
|
||||
if (d.isString(b)) {
|
||||
b.length;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
class A {
|
||||
propA: number;
|
||||
isA(): this is A {
|
||||
return true;
|
||||
}
|
||||
isC(): this is C {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
propB: number;
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isC(): this is C {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class C extends A {
|
||||
propC: number;
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isC(): this is C {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
isA(): this is A {
|
||||
return false;
|
||||
}
|
||||
isString(x: any): x is string { // with parameter declaration
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var a: A;
|
||||
|
||||
// Basic.
|
||||
if (a.isC()) {
|
||||
a.propC;
|
||||
}
|
||||
|
||||
// Sub type.
|
||||
var subType: C;
|
||||
if(subType.isA()) {
|
||||
subType.propC;
|
||||
}
|
||||
|
||||
// Union type.
|
||||
var union: A | B;
|
||||
if(union.isA()) {
|
||||
union.propA;
|
||||
}
|
||||
|
||||
var b: any;
|
||||
var d = new D;
|
||||
if(d.isString(b)) {
|
||||
b.length;
|
||||
}
|
Loading…
Reference in a new issue