Property variance type annotations for Flow plugin (#161)
* Property variance type annotations for Flow plugin Non-method properties and indexers of object types, declare class, and interfaces can be "positive" or "negative." Class fields, but again not methods, can also have variance. This PR generalizes the variance annotations for type parameters into a new node type, and reuses that node for those properties. The code for object types is reused for interfaces and declare classes. The changes there are straightfoward. The code for class fields is reused for object literals, which do not support variance annotations (currently). This code is a bit sketchy, because we always parse variance annotations in the `parsePropertyName` extension, then error in a the subsequent parse phase for object literals (`parseObjPropValue`) or class methods (`parseClassMethod`). * Remove bogus unreachable code, clarify variance parsing conditional * Don't use a new node type for variance annotations Adding a new node type, specifically changing the TypeParameter node's variance property to be node-valued, is a breaking change. We might choose to make this breaking change in a later version. * s/start/variancePos
This commit is contained in:
committed by
Daniel Tschinder
parent
b5877f04b1
commit
26809e8ce7
@@ -205,15 +205,7 @@ pp.flowParseTypeAlias = function (node) {
|
||||
pp.flowParseTypeParameter = function () {
|
||||
let node = this.startNode();
|
||||
|
||||
let variance;
|
||||
if (this.match(tt.plusMin)) {
|
||||
if (this.state.value === "+") {
|
||||
variance = "plus";
|
||||
} else if (this.state.value === "-") {
|
||||
variance = "minus";
|
||||
}
|
||||
this.eat(tt.plusMin);
|
||||
}
|
||||
let variance = this.flowParseVariance();
|
||||
|
||||
let ident = this.flowParseTypeAnnotatableIdentifier(false, false);
|
||||
node.name = ident.name;
|
||||
@@ -278,7 +270,7 @@ pp.flowParseObjectPropertyKey = function () {
|
||||
return (this.match(tt.num) || this.match(tt.string)) ? this.parseExprAtom() : this.parseIdentifier(true);
|
||||
};
|
||||
|
||||
pp.flowParseObjectTypeIndexer = function (node, isStatic) {
|
||||
pp.flowParseObjectTypeIndexer = function (node, isStatic, variance) {
|
||||
node.static = isStatic;
|
||||
|
||||
this.expect(tt.bracketL);
|
||||
@@ -286,6 +278,7 @@ pp.flowParseObjectTypeIndexer = function (node, isStatic) {
|
||||
node.key = this.flowParseTypeInitialiser();
|
||||
this.expect(tt.bracketR);
|
||||
node.value = this.flowParseTypeInitialiser();
|
||||
node.variance = variance;
|
||||
|
||||
this.flowObjectTypeSemicolon();
|
||||
return this.finishNode(node, "ObjectTypeIndexer");
|
||||
@@ -371,9 +364,15 @@ pp.flowParseObjectType = function (allowStatic, allowExact) {
|
||||
isStatic = true;
|
||||
}
|
||||
|
||||
let variancePos = this.state.start;
|
||||
let variance = this.flowParseVariance();
|
||||
|
||||
if (this.match(tt.bracketL)) {
|
||||
nodeStart.indexers.push(this.flowParseObjectTypeIndexer(node, isStatic));
|
||||
nodeStart.indexers.push(this.flowParseObjectTypeIndexer(node, isStatic, variance));
|
||||
} else if (this.match(tt.parenL) || this.isRelational("<")) {
|
||||
if (variance) {
|
||||
this.unexpected(variancePos);
|
||||
}
|
||||
nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, allowStatic));
|
||||
} else {
|
||||
if (isStatic && this.match(tt.colon)) {
|
||||
@@ -383,6 +382,9 @@ pp.flowParseObjectType = function (allowStatic, allowExact) {
|
||||
}
|
||||
if (this.isRelational("<") || this.match(tt.parenL)) {
|
||||
// This is a method property
|
||||
if (variance) {
|
||||
this.unexpected(variancePos);
|
||||
}
|
||||
nodeStart.properties.push(this.flowParseObjectTypeMethod(startPos, startLoc, isStatic, propertyKey));
|
||||
} else {
|
||||
if (this.eat(tt.question)) {
|
||||
@@ -392,6 +394,7 @@ pp.flowParseObjectType = function (allowStatic, allowExact) {
|
||||
node.value = this.flowParseTypeInitialiser();
|
||||
node.optional = optional;
|
||||
node.static = isStatic;
|
||||
node.variance = variance;
|
||||
this.flowObjectTypeSemicolon();
|
||||
nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty"));
|
||||
}
|
||||
@@ -736,6 +739,19 @@ pp.typeCastToParameter = function (node) {
|
||||
);
|
||||
};
|
||||
|
||||
pp.flowParseVariance = function() {
|
||||
let variance = null;
|
||||
if (this.match(tt.plusMin)) {
|
||||
if (this.state.value === "+") {
|
||||
variance = "plus";
|
||||
} else if (this.state.value === "-") {
|
||||
variance = "minus";
|
||||
}
|
||||
this.next();
|
||||
}
|
||||
return variance;
|
||||
};
|
||||
|
||||
export default function (instance) {
|
||||
// plain function return types: function name(): string {}
|
||||
instance.extend("parseFunctionBody", function (inner) {
|
||||
@@ -990,6 +1006,7 @@ export default function (instance) {
|
||||
// parse class property type annotations
|
||||
instance.extend("parseClassProperty", function (inner) {
|
||||
return function (node) {
|
||||
delete node.variancePos;
|
||||
if (this.match(tt.colon)) {
|
||||
node.typeAnnotation = this.flowParseTypeAnnotation();
|
||||
}
|
||||
@@ -1007,6 +1024,11 @@ export default function (instance) {
|
||||
// parse type parameters for class methods
|
||||
instance.extend("parseClassMethod", function () {
|
||||
return function (classBody, method, isGenerator, isAsync) {
|
||||
if (method.variance) {
|
||||
this.unexpected(method.variancePos);
|
||||
}
|
||||
delete method.variance;
|
||||
delete method.variancePos;
|
||||
if (this.isRelational("<")) {
|
||||
method.typeParameters = this.flowParseTypeParameterDeclaration();
|
||||
}
|
||||
@@ -1039,9 +1061,26 @@ export default function (instance) {
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parsePropertyName", function (inner) {
|
||||
return function (node) {
|
||||
let variancePos = this.state.start;
|
||||
let variance = this.flowParseVariance();
|
||||
let key = inner.call(this, node);
|
||||
node.variance = variance;
|
||||
node.variancePos = variancePos;
|
||||
return key;
|
||||
};
|
||||
});
|
||||
|
||||
// parse type parameters for object method shorthand
|
||||
instance.extend("parseObjPropValue", function (inner) {
|
||||
return function (prop) {
|
||||
if (prop.variance) {
|
||||
this.unexpected(prop.variancePos);
|
||||
}
|
||||
delete prop.variance;
|
||||
delete prop.variancePos;
|
||||
|
||||
let typeParameters;
|
||||
|
||||
// method shorthand
|
||||
|
||||
Reference in New Issue
Block a user