Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript Constant contexts #9534

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions packages/babel-parser/src/plugins/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,44 @@ export default (superClass: Class<Parser>): Class<Parser> =>
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) {
Andarist marked this conversation as resolved.
Show resolved Hide resolved
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`,
// but here it's always false, because this is only used for types.
tsFillSignature(
Expand Down Expand Up @@ -937,12 +975,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>

tsParseTypeAssertion(): N.TsTypeAssertion {
const node: N.TsTypeAssertion = this.startNode();
this.next(); // <
// Not actually necessary to set state.inType because we never reach here if JSX plugin is enabled,
// but need `tsInType` to satisfy the assertion in `tsParseType`.
node.typeAnnotation = this.tsInType(() => this.tsParseType());
const _const = this.tsTryNextParseConstantContext();
node.typeAnnotation = _const || this.tsNextThenParseType();
this.expectRelational(">");
node.expression = this.parseMaybeUnary();
if (_const) {
this.tsCheckLiteralForConstantContext(node.expression);
}
return this.finishNode(node, "TSTypeAssertion");
}

Expand Down Expand Up @@ -1605,7 +1644,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
leftStartLoc,
);
node.expression = left;
node.typeAnnotation = this.tsNextThenParseType();
const _const = this.tsTryNextParseConstantContext();
if (_const) {
this.tsCheckLiteralForConstantContext(node.expression);
node.typeAnnotation = _const;
} else {
node.typeAnnotation = this.tsNextThenParseType();
}
this.finishNode(node, "TSAsExpression");
return this.parseExprOp(
node,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let e = v as const; // Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:8)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let e = (true ? 1 : 0) as const; // Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:9)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let e = id(1) as const; // Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:8)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let e = [v()] as const; // Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
"typescript"
],
"throws": "Only literal values are allowed in constant contexts (1:9)"
}
Original file line number Diff line number Diff line change
@@ -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 };
Loading