-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
+948
−113
Merged
Changes from 14 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
bd0ce7b
`const` modifier on type parameters + revised contextual type logic
ahejlsberg 7c48b0d
Accept new baselines
ahejlsberg 88c7149
Fix modifier checking
ahejlsberg ccf252f
Add tests
ahejlsberg f8fd1fd
Cache isConstTypeVariable check
ahejlsberg c3fcf6e
Revert "Cache isConstTypeVariable check"
ahejlsberg 0507bf0
Fewer isConstTypeParameterContext checks
ahejlsberg b56d187
Pay attention to cached `undefined` contextual type
ahejlsberg 54f649d
Allow `const` modifier in more places + properly print back
ahejlsberg d2af941
Also permit `const` in method signature type parameters
ahejlsberg a315761
Fix parsing of `const` modifier in array expression type parameters
ahejlsberg f6bbf9c
Accept new baselines
ahejlsberg 636d937
Merge branch 'main' into fix30680
ahejlsberg dccb290
Remove unused properties from NodeLinks
ahejlsberg fde9c47
Rename `permitInvalidConstAsModifier` to `permitConstAsModifier`
ahejlsberg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -2814,7 +2814,7 @@ namespace Parser { | |||||||
case ParsingContext.ArrayBindingElements: | ||||||||
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: | ||||||||
|
@@ -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; | ||||||||
|
@@ -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(); | ||||||||
|
@@ -7742,10 +7743,10 @@ namespace Parser { | |||||||
* | ||||||||
* In such situations, 'permitInvalidConstAsModifier' should be set to true. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
*/ | ||||||||
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); | ||||||||
} | ||||||||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
tests/baselines/reference/typeParameterConstModifiers.errors.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
100
tests/baselines/reference/typeParameterConstModifiers.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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:TS playground
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now fixed.