TypeScript Constant contexts (#9534)

* Add as const fixtures

* parse constant context

* parse constant context

* quick check for literal

* code review, better error message, check array elements

* Removed unneeded test cases
This commit is contained in:
Tan Li Hau 2019-03-16 13:40:39 +08:00 committed by Nicolò Ribaudo
parent 0a69b45126
commit 25a3825a1d
13 changed files with 5697 additions and 5 deletions

View File

@ -321,6 +321,44 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(node, "TSTypeParameterDeclaration"); return this.finishNode(node, "TSTypeParameterDeclaration");
} }
tsTryNextParseConstantContext(): ?N.TsTypeReference {
if (this.lookahead().type === tt._const) {
this.next();
return this.tsParseTypeReference();
}
return null;
}
tsCheckLiteralForConstantContext(node: N.Node) {
switch (node.type) {
case "StringLiteral":
case "TemplateLiteral":
case "NumericLiteral":
case "BooleanLiteral":
case "SpreadElement":
case "ObjectMethod":
case "ObjectExpression":
return;
case "ArrayExpression":
return (node: N.ArrayExpression).elements.forEach(element => {
if (element) {
this.tsCheckLiteralForConstantContext(element);
}
});
case "ObjectProperty":
return this.tsCheckLiteralForConstantContext(
(node: N.ObjectProperty).value,
);
case "UnaryExpression":
return this.tsCheckLiteralForConstantContext(node.argument);
default:
this.raise(
node.start,
"Only literal values are allowed in constant contexts",
);
}
}
// Note: In TypeScript implementation we must provide `yieldContext` and `awaitContext`, // Note: In TypeScript implementation we must provide `yieldContext` and `awaitContext`,
// but here it's always false, because this is only used for types. // but here it's always false, because this is only used for types.
tsFillSignature( tsFillSignature(
@ -937,12 +975,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseTypeAssertion(): N.TsTypeAssertion { tsParseTypeAssertion(): N.TsTypeAssertion {
const node: N.TsTypeAssertion = this.startNode(); const node: N.TsTypeAssertion = this.startNode();
this.next(); // < const _const = this.tsTryNextParseConstantContext();
// Not actually necessary to set state.inType because we never reach here if JSX plugin is enabled, node.typeAnnotation = _const || this.tsNextThenParseType();
// but need `tsInType` to satisfy the assertion in `tsParseType`.
node.typeAnnotation = this.tsInType(() => this.tsParseType());
this.expectRelational(">"); this.expectRelational(">");
node.expression = this.parseMaybeUnary(); node.expression = this.parseMaybeUnary();
if (_const) {
this.tsCheckLiteralForConstantContext(node.expression);
}
return this.finishNode(node, "TSTypeAssertion"); return this.finishNode(node, "TSTypeAssertion");
} }
@ -1605,7 +1644,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
leftStartLoc, leftStartLoc,
); );
node.expression = left; node.expression = left;
const _const = this.tsTryNextParseConstantContext();
if (_const) {
this.tsCheckLiteralForConstantContext(node.expression);
node.typeAnnotation = _const;
} else {
node.typeAnnotation = this.tsNextThenParseType(); node.typeAnnotation = this.tsNextThenParseType();
}
this.finishNode(node, "TSAsExpression"); this.finishNode(node, "TSAsExpression");
return this.parseExprOp( return this.parseExprOp(
node, node,

View File

@ -0,0 +1 @@
let e = v as const; // Error

View File

@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:8)"
}

View File

@ -0,0 +1 @@
let e = (true ? 1 : 0) as const; // Error

View File

@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:9)"
}

View File

@ -0,0 +1 @@
let e = id(1) as const; // Error

View File

@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:8)"
}

View File

@ -0,0 +1 @@
let e = [v()] as const; // Error

View File

@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:9)"
}

View File

@ -0,0 +1,39 @@
// Copied over from TypeScript's test case
// https://github.com/Microsoft/TypeScript/blob/master/tests/baselines/reference/constAssertions.js
let v1 = 'abc' as const;
let v2 = `abc` as const;
let v3 = 10 as const;
let v4 = -10 as const;
let v5 = +10 as const;
let v6 = 10 as const;
let v7 = -10 as const;
let v8 = true as const;
let v9 = false as const;
let a1 = [] as const;
let a2 = [1, 2, 3] as const;
let a3 = [10, 'hello', true] as const;
let a4 = [...[1, 2, 3]] as const;
let a5 = [1, 2, 3];
let a6 = [...a5] as const;
let a8 = ['abc', ...a7] as const;
let o1 = { x: 10, y: 20 } as const;
let o2 = { a: 1, 'b': 2, ['c']: 3, d() {}, ['e' + '']: 4 } as const;
let o3 = { ...o1, ...o2 } as const;
let o5 = { ...o4 } as const;
let o7 = { ...d } as const;
let o9 = { x: 10, foo() { this.x = 20 } } as const; // Error
let p1 = (10) as const;
let p2 = ((-10)) as const;
let p3 = ([(10)]) as const;
let p4 = [[[[10]]]] as const;
let x1 = { x: 10, y: [20, 30], z: { a: { b: 42 } } } as const;
let q1 = <const> 10;
let q2 = <const> 'abc';
let q3 = <const> true;
let q4 = <const> [1, 2, 3];
let q5 = <const> { x: 10, y: 20 };

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
const b: const;

View File

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