Disallow flow type parameter defaults in some cases

This commit is contained in:
Brian Ng 2018-04-26 11:02:27 -05:00
parent 65ca968f8b
commit 0353ce9ed5
16 changed files with 93 additions and 57 deletions

View File

@ -505,7 +505,18 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// Type annotations
flowParseTypeParameter(): N.TypeParameter {
flowParseTypeParameter(
allowDefault?: boolean = true,
requireDefault?: boolean = false,
): N.TypeParameter {
if (!allowDefault && requireDefault) {
throw new Error(
"Cannot disallow a default value (`allowDefault`) while also requiring it (`requireDefault`).",
);
}
const nodeStart = this.state.start;
const node = this.startNode();
const variance = this.flowParseVariance();
@ -516,14 +527,28 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.bound = ident.typeAnnotation;
if (this.match(tt.eq)) {
this.eat(tt.eq);
node.default = this.flowParseType();
if (allowDefault) {
this.eat(tt.eq);
node.default = this.flowParseType();
} else {
this.unexpected();
}
} else {
if (requireDefault) {
this.unexpected(
nodeStart,
// eslint-disable-next-line max-len
"Type parameter declaration needs a default, since a preceding type parameter declaration has a default.",
);
}
}
return this.finishNode(node, "TypeParameter");
}
flowParseTypeParameterDeclaration(): N.TypeParameterDeclaration {
flowParseTypeParameterDeclaration(
allowDefault?: boolean = true,
): N.TypeParameterDeclaration {
const oldInType = this.state.inType;
const node = this.startNode();
node.params = [];
@ -537,8 +562,20 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.unexpected();
}
let defaultRequired = false;
do {
node.params.push(this.flowParseTypeParameter());
const typeParameter = this.flowParseTypeParameter(
allowDefault,
defaultRequired,
);
node.params.push(typeParameter);
if (typeParameter.default) {
defaultRequired = true;
}
if (!this.isRelational(">")) {
this.expect(tt.comma);
}
@ -607,7 +644,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.typeParameters = null;
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
node.typeParameters = this.flowParseTypeParameterDeclaration(
/* allowDefault */ false,
);
}
this.expect(tt.parenL);
@ -668,16 +707,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
while (!this.match(endDelim)) {
let isStatic = false;
const node = this.startNode();
const lookahead = this.lookahead();
if (
allowStatic &&
this.isContextual("static") &&
if (allowStatic && this.isContextual("static")) {
const lookahead = this.lookahead();
// static is a valid identifier name
(lookahead.type !== tt.colon && lookahead.type !== tt.question)
) {
this.next();
isStatic = true;
if (lookahead.type !== tt.colon && lookahead.type !== tt.question) {
this.next();
isStatic = true;
}
}
const variance = this.flowParseVariance();
@ -999,7 +1037,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
case tt.relational:
if (this.state.value === "<") {
node.typeParameters = this.flowParseTypeParameterDeclaration();
node.typeParameters = this.flowParseTypeParameterDeclaration(
/* allowDefault */ false,
);
this.expect(tt.parenL);
tmp = this.flowParseFunctionTypeParams();
node.params = tmp.params;
@ -1777,7 +1817,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
delete (method: $FlowFixMe).variance;
if (this.isRelational("<")) {
method.typeParameters = this.flowParseTypeParameterDeclaration();
method.typeParameters = this.flowParseTypeParameterDeclaration(
/* allowDefault */ false,
);
}
super.pushClassMethod(
@ -1858,7 +1900,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// method shorthand
if (this.isRelational("<")) {
typeParameters = this.flowParseTypeParameterDeclaration();
typeParameters = this.flowParseTypeParameterDeclaration(
/* allowDefault */ false,
);
if (!this.match(tt.parenL)) this.unexpected();
}
@ -2057,7 +2101,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// $FlowFixMe
const kind = node.kind;
if (kind !== "get" && kind !== "set" && this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
node.typeParameters = this.flowParseTypeParameterDeclaration(
/* allowDefault */ false,
);
}
super.parseFunctionParams(node);
}

View File

@ -856,6 +856,7 @@ export type TypeParameterBase = NodeBase & {
export type TypeParameter = TypeParameterBase & {
type: "TypeParameter",
default?: TypeAnnotation,
};
export type TsTypeParameter = TypeParameterBase & {

View File

@ -1,33 +0,0 @@
{
"type": "File",
"start": 0,
"end": 0,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 0
}
},
"program": {
"type": "Program",
"start": 0,
"end": 0,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 0
}
},
"sourceType": "module",
"body": [],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
(class A {
foo<T = string>() {}
});

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (2:8)"
}

View File

@ -0,0 +1 @@
({ foo<T = string>() {} });

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:9)"
}

View File

@ -0,0 +1 @@
declare class A { foo<T = string>(): void }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:24)"
}

View File

@ -0,0 +1,3 @@
class A {
foo<T = string>() {}
}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (2:8)"
}

View File

@ -0,0 +1 @@
function foo<T = string>() {}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:15)"
}

View File

@ -0,0 +1 @@
type A<HasDefault = string, NoDefault> = T;

View File

@ -0,0 +1,3 @@
{
"throws": "Type parameter declaration needs a default, since a preceding type parameter declaration has a default. (1:28)"
}

View File

@ -28,12 +28,6 @@ types/annotations/migrated_0001.js
types/annotations_in_comments_invalid/migrated_0003.js
types/annotations/void_is_reserved_param.js
types/member/reserved_words.js
types/parameter_defaults/migrated_0023.js
types/parameter_defaults/migrated_0026.js
types/parameter_defaults/migrated_0028.js
types/parameter_defaults/migrated_0029.js
types/parameter_defaults/migrated_0030.js
types/parameter_defaults/migrated_0031.js
types/parameter_defaults/migrated_0032.js
types/typecasts_invalid/migrated_0001.js
class_method_kinds/polymorphic_getter.js