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

const modifier on type parameters #51865

Merged
merged 15 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from 14 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
212 changes: 111 additions & 101 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,10 @@
"category": "Error",
"code": 1276
},
"'{0}' modifier can only appear on a type parameter of a function, method or class": {
"category": "Error",
"code": 1277
},

"'with' statements are not allowed in an async function block.": {
"category": "Error",
Expand Down
11 changes: 6 additions & 5 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2814,7 +2814,7 @@ namespace Parser {
case ParsingContext.ArrayBindingElements:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the type parameter modifiers be available for arrow functions when JSX syntax is enabled? in/out also don't work - but it's kinda surprising that even an extra comma doesn't make those parseable:

const arrow = <T, >(a: T) => {} // ok
const varianceAnnotation = <out T, >(a: T) => {} // fails to parse
const constModifier = <const T, >(a: T) => {} // fails to parse

TS playground

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah, that doesn't look right.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now fixed.

return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isBindingIdentifierOrPrivateIdentifierOrPattern();
case ParsingContext.TypeParameters:
return token() === SyntaxKind.InKeyword || isIdentifier();
return token() === SyntaxKind.InKeyword || token() === SyntaxKind.ConstKeyword || isIdentifier();
case ParsingContext.ArrayLiteralMembers:
switch (token()) {
case SyntaxKind.CommaToken:
Expand Down Expand Up @@ -3823,7 +3823,7 @@ namespace Parser {

function parseTypeParameter(): TypeParameterDeclaration {
const pos = getNodePos();
const modifiers = parseModifiers();
const modifiers = parseModifiers(/*permitConstAsModifier*/ true);
const name = parseIdentifier();
let constraint: TypeNode | undefined;
let expression: Expression | undefined;
Expand Down Expand Up @@ -5203,13 +5203,14 @@ namespace Parser {

// If we have "<" not followed by an identifier,
// then this definitely is not an arrow function.
if (!isIdentifier()) {
if (!isIdentifier() && token() !== SyntaxKind.ConstKeyword) {
return Tristate.False;
}

// JSX overrides
if (languageVariant === LanguageVariant.JSX) {
const isArrowFunctionInJsx = lookAhead(() => {
parseOptional(SyntaxKind.ConstKeyword);
const third = nextToken();
if (third === SyntaxKind.ExtendsKeyword) {
const fourth = nextToken();
Expand Down Expand Up @@ -7742,10 +7743,10 @@ namespace Parser {
*
* In such situations, 'permitInvalidConstAsModifier' should be set to true.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* In such situations, 'permitInvalidConstAsModifier' should be set to true.
* In such situations, 'permitConstAsModifier' should be set to true.

*/
function parseModifiers(permitInvalidConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean): NodeArray<Modifier> | undefined {
function parseModifiers(permitConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean): NodeArray<Modifier> | undefined {
const pos = getNodePos();
let list, modifier, hasSeenStatic = false;
while (modifier = tryParseModifier(permitInvalidConstAsModifier, stopOnStartOfClassStaticBlock, hasSeenStatic)) {
while (modifier = tryParseModifier(permitConstAsModifier, stopOnStartOfClassStaticBlock, hasSeenStatic)) {
if (modifier.kind === SyntaxKind.StaticKeyword) hasSeenStatic = true;
list = append(list, modifier);
}
Expand Down
3 changes: 0 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5958,9 +5958,6 @@ export interface NodeLinks {
skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type
declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter.
serializedTypes?: Map<string, SerializedTypeEntry>; // Collection of types serialized at this location

contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution
inferenceContext?: InferenceContext; // Inference context for contextual type
}

/** @internal */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ Info 32 [00:01:13.000] response:
"1274",
"1275",
"1276",
"1277",
"1300",
"1309",
"1313",
Expand Down Expand Up @@ -1949,6 +1950,7 @@ Info 38 [00:01:19.000] response:
"1274",
"1275",
"1276",
"1277",
"1300",
"1309",
"1313",
Expand Down Expand Up @@ -3187,6 +3189,7 @@ Info 40 [00:01:21.000] response:
"1274",
"1275",
"1276",
"1277",
"1300",
"1309",
"1313",
Expand Down
80 changes: 80 additions & 0 deletions tests/baselines/reference/typeParameterConstModifiers.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts(43,14): error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class
tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts(49,9): error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class


==== tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts (2 errors) ====
declare function f1<const T>(x: T): T;

const x11 = f1('a');
const x12 = f1(['a', ['b', 'c']]);
const x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });

declare function f2<const T, U>(x: T | undefined): T;

const x21 = f2('a');
const x22 = f2(['a', ['b', 'c']]);
const x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });

declare function f3<const T>(x: T): T[];

const x31 = f3("hello");
const x32 = f3("hello");

declare function f4<const T>(obj: [T, T]): T;

const x41 = f4([[1, 'x'], [2, 'y']]);
const x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]);

declare function f5<const T>(obj: { x: T, y: T }): T;

const x51 = f5({ x: [1, 'x'], y: [2, 'y'] });
const x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } });

declare function f6<const T extends readonly unknown[]>(...args: T): T;

const x61 = f6(1, 'b', { a: 1, b: 'x' });

class C1<const T> {
constructor(x: T) {}
foo<const U>(x: U) { return x; }
}

const c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
const c72 = c71.foo(['a', ['b', 'c']]);

const fx1 = <const T>(x: T) => x;
const fx2 = <const T,>(x: T) => x;

interface I1<const T> { x: T } // Error
~~~~~
!!! error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class

interface I2 {
f<const T>(x: T): T;
}

type T1<const T> = T; // Error
~~~~~
!!! error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class

type T2 = <const T>(x: T) => T;
type T3 = { <const T>(x: T): T };
type T4 = new <const T>(x: T) => T;
type T5 = { new <const T>(x: T): T };

// Corrected repro from #51745

type Obj = { a: { b: { c: "123" } } };

type GetPath<T, P> =
P extends readonly [] ? T :
P extends readonly [infer A extends keyof T, ...infer Rest] ? GetPath<T[A], Rest> :
never;

function set<T, const P extends readonly string[]>(obj: T, path: P, value: GetPath<T, P>) {}

declare let obj: Obj;
declare let value: "123";

set(obj, ['a', 'b', 'c'], value);

100 changes: 100 additions & 0 deletions tests/baselines/reference/typeParameterConstModifiers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//// [typeParameterConstModifiers.ts]
declare function f1<const T>(x: T): T;

const x11 = f1('a');
const x12 = f1(['a', ['b', 'c']]);
const x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });

declare function f2<const T, U>(x: T | undefined): T;

const x21 = f2('a');
const x22 = f2(['a', ['b', 'c']]);
const x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });

declare function f3<const T>(x: T): T[];

const x31 = f3("hello");
const x32 = f3("hello");

declare function f4<const T>(obj: [T, T]): T;

const x41 = f4([[1, 'x'], [2, 'y']]);
const x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]);

declare function f5<const T>(obj: { x: T, y: T }): T;

const x51 = f5({ x: [1, 'x'], y: [2, 'y'] });
const x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } });

declare function f6<const T extends readonly unknown[]>(...args: T): T;

const x61 = f6(1, 'b', { a: 1, b: 'x' });

class C1<const T> {
constructor(x: T) {}
foo<const U>(x: U) { return x; }
}

const c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
const c72 = c71.foo(['a', ['b', 'c']]);

const fx1 = <const T>(x: T) => x;
const fx2 = <const T,>(x: T) => x;

interface I1<const T> { x: T } // Error

interface I2 {
f<const T>(x: T): T;
}

type T1<const T> = T; // Error

type T2 = <const T>(x: T) => T;
type T3 = { <const T>(x: T): T };
type T4 = new <const T>(x: T) => T;
type T5 = { new <const T>(x: T): T };

// Corrected repro from #51745

type Obj = { a: { b: { c: "123" } } };

type GetPath<T, P> =
P extends readonly [] ? T :
P extends readonly [infer A extends keyof T, ...infer Rest] ? GetPath<T[A], Rest> :
never;

function set<T, const P extends readonly string[]>(obj: T, path: P, value: GetPath<T, P>) {}

declare let obj: Obj;
declare let value: "123";

set(obj, ['a', 'b', 'c'], value);


//// [typeParameterConstModifiers.js]
"use strict";
var x11 = f1('a');
var x12 = f1(['a', ['b', 'c']]);
var x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
var x21 = f2('a');
var x22 = f2(['a', ['b', 'c']]);
var x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
var x31 = f3("hello");
var x32 = f3("hello");
var x41 = f4([[1, 'x'], [2, 'y']]);
var x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]);
var x51 = f5({ x: [1, 'x'], y: [2, 'y'] });
var x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } });
var x61 = f6(1, 'b', { a: 1, b: 'x' });
var C1 = /** @class */ (function () {
function C1(x) {
}
C1.prototype.foo = function (x) { return x; };
return C1;
}());
var c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
var c72 = c71.foo(['a', ['b', 'c']]);
var fx1 = function (x) { return x; };
var fx2 = function (x) { return x; };
function set(obj, path, value) { }
set(obj, ['a', 'b', 'c'], value);
Loading