Skip to content

Commit da29c99

Browse files
authored
Merge branch 'main' into gabritto/jsxchildren
2 parents 8786cc5 + 6e519c5 commit da29c99

File tree

89 files changed

+910
-170
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+910
-170
lines changed

.github/copilot-instructions.md

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
1-
# TypeScript Test Writing Guide for Copilot
1+
# Guide for Copilot
22

33
This document provides a concise guide for writing TypeScript fourslash tests and compiler tests, along with build instructions.
44

55
## Build Instructions Summary
66

77
### Setup
88
1. Install Node.js (current or LTS)
9-
2. Install hereby: `npm install -g hereby`
10-
3. Clone the repository: `git clone --depth=1 https://github.com/microsoft/TypeScript`
11-
4. Install dependencies: `npm ci`
9+
2. Clone the repository: `git clone --depth=1 https://github.com/microsoft/TypeScript`
10+
3. Install dependencies: `npm ci`
1211

1312
### Common Build Tasks
1413
```bash
15-
hereby local # Build the compiler into built/local
16-
hereby clean # Delete the built compiler
17-
hereby tests # Build the test infrastructure
18-
hereby runtests # Run all tests
19-
hereby runtests-parallel # Run tests in parallel (recommended)
20-
hereby runtests --runner=fourslash # Run only fourslash tests
21-
hereby runtests --runner=compiler # Run only compiler tests
22-
hereby runtests --tests=<testPath> # Run specific test
23-
hereby baseline-accept # Accept new test baselines
24-
hereby lint # Run eslint
25-
hereby format # Run code formatting
14+
npx hereby local # Build the compiler into built/local
15+
npx hereby clean # Delete the built compiler
16+
npx hereby tests # Build the test infrastructure
17+
npx hereby runtests # Run all tests
18+
npx hereby runtests-parallel # Run tests in parallel (recommended)
19+
npx hereby runtests --runner=fourslash # Run only fourslash tests
20+
npx hereby runtests --runner=compiler # Run only compiler tests
21+
npx hereby runtests --tests=<testPath> # Run specific test
22+
npx hereby baseline-accept # Accept new test baselines
23+
npx hereby lint # Run eslint
24+
npx hereby format # Run code formatting
2625
```
2726

2827
## Fourslash Test Syntax Guide
@@ -248,30 +247,57 @@ const config3: Config = { optional: 42 }; // Should error - missing required
248247

249248
```bash
250249
# Run a specific fourslash test
251-
hereby runtests --tests=tests/cases/fourslash/completionForObjectProperty.ts
250+
npx hereby runtests --tests=tests/cases/fourslash/completionForObjectProperty.ts
252251

253252
# Run a specific compiler test
254-
hereby runtests --tests=tests/cases/compiler/abstractClassUnionInstantiation.ts
253+
npx hereby runtests --tests=tests/cases/compiler/abstractClassUnionInstantiation.ts
255254

256255
# Run tests matching a pattern
257-
hereby runtests --tests=tests/cases/fourslash/completion*.ts
256+
npx hereby runtests --tests=tests/cases/fourslash/completion*.ts
258257
```
259258

260259
## Important Guidelines
261260

261+
### Keeping Things Tidy
262+
263+
- Once you think you're done, run `npx hereby lint` and fix any issues
264+
- Then always run `npx hereby format` as your last step
265+
262266
### Test Locations
267+
263268
- Only add testcases in `tests/cases/compiler` or `tests/cases/fourslash`
269+
- Filenames in `tests/cases/compiler` must always end with `.ts`, not `.d.ts`
264270
- Do not write direct unit tests as they are almost never the correct test format for our repo
265271

266272
### Performance Expectations
273+
267274
- Running a set of tests may take up to 4 minutes
268275
- A full test run may take up to 15 minutes
269-
- Always run `hereby lint` and `hereby format` before you're done
270276

271277
### Working with Issues
278+
272279
- Maintainer comments in the issue should generally take priority over OP's comments
273280
- Maintainers might give you hints on where to start. They are not always right, but a good place to start
274281

282+
### Debugging Tips
283+
284+
printf debugging is going to be very useful as you are figuring things out.
285+
To do this, use `console.log`, but you'll need to `ts-ignore` it.
286+
Write something like this:
287+
```ts,diff
288+
function checkSomething(n: Node) {
289+
doSomething(n);
290+
+ // @ts-ignore DEBUG CODE ONLY, REMOVE ME WHEN DONE
291+
+ console.log(`Got node with pos = ${n.pos}`);
292+
doSomethingElse(n);
293+
}
294+
```
295+
We have a lot of enums so you might want to print back their symbolic name, to do this, index back into the name of the enum
296+
```ts
297+
// @ts-ignore DEBUG CODE ONLY, REMOVE ME WHEN DONE
298+
console.log(`Got node with kind = ${SyntaxKind[n.kind]}`);
299+
```
300+
275301
## Recommended Workflow
276302

277303
When fixing bugs or implementing features, follow this workflow:
@@ -287,6 +313,15 @@ When fixing bugs or implementing features, follow this workflow:
287313
- Ensure the baselines change in a way that demonstrates that the bug is fixed
288314
- Put this baseline diff in its own commit
289315

290-
4. **Run all other tests to ensure you didn't break anything**
291-
- Some collateral baseline changes are normal
316+
4. **Add more testing**
317+
- Once you've got the basics figured out, enhance your test to cover edge cases and other variations
318+
- Run the test again and commit the baseline diff along with the test edit
319+
320+
5. **Run all other tests to ensure you didn't break anything**
321+
- Run `npx hereby runtests-parallel` and wait for it to finish (10-15 minutes is normal!)
322+
- Some collateral baseline changes are normal, but review for correctness
292323
- Put these diffs in another commit
324+
325+
6. **Always format and lint**
326+
- Don't forget to run `npx hereby lint` and `npx hereby format` before you're done
327+
- Double-check your line endings. Source files in this repo typically use CRLF line endings. Fix all line endings to be consistent before you wrap up
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: 'Copilot Setup Steps'
2+
on: workflow_dispatch
3+
4+
jobs:
5+
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
6+
copilot-setup-steps:
7+
runs-on: ubuntu-latest
8+
9+
# Set the permissions to the lowest permissions possible needed for your steps.
10+
# Copilot will be given its own token for its operations.
11+
permissions:
12+
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
13+
contents: read
14+
15+
# You can define any steps you want, and they will run before the agent starts.
16+
# If you do not check out your code, Copilot will do this for you.
17+
steps:
18+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
19+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
20+
- run: npm ci
21+
# pull dprint caches before network access is blocked
22+
- run: npx hereby check:format || true

src/compiler/checker.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25340,11 +25340,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2534025340
// right is a supertype.
2534125341
const superTypeOrUnion = literalTypesWithSameBaseType(primaryTypes) ?
2534225342
getUnionType(primaryTypes) :
25343-
reduceLeft(primaryTypes, (s, t) => isTypeSubtypeOf(s, t) ? t : s)!;
25343+
getSingleCommonSupertype(primaryTypes);
2534425344
// Add any nullable types that occurred in the candidates back to the result.
2534525345
return primaryTypes === types ? superTypeOrUnion : getNullableType(superTypeOrUnion, getCombinedTypeFlags(types) & TypeFlags.Nullable);
2534625346
}
2534725347

25348+
function getSingleCommonSupertype(types: Type[]) {
25349+
// First, find the leftmost type for which no type to the right is a strict supertype, and if that
25350+
// type is a strict supertype of all other candidates, return it. Otherwise, return the leftmost type
25351+
// for which no type to the right is a (regular) supertype.
25352+
const candidate = reduceLeft(types, (s, t) => isTypeStrictSubtypeOf(s, t) ? t : s)!;
25353+
return every(types, t => t === candidate || isTypeStrictSubtypeOf(t, candidate)) ?
25354+
candidate :
25355+
reduceLeft(types, (s, t) => isTypeSubtypeOf(s, t) ? t : s)!;
25356+
}
25357+
2534825358
// Return the leftmost type for which no type to the right is a subtype.
2534925359
function getCommonSubtype(types: Type[]) {
2535025360
return reduceLeft(types, (s, t) => isTypeSubtypeOf(t, s) ? t : s)!;
@@ -53029,7 +53039,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5302953039
if (languageVersion < ScriptTarget.ES2015 && isPrivateIdentifier(node.name)) {
5303053040
return grammarErrorOnNode(node.name, Diagnostics.Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher);
5303153041
}
53032-
if (languageVersion < ScriptTarget.ES2015 && isAutoAccessorPropertyDeclaration(node)) {
53042+
if (languageVersion < ScriptTarget.ES2015 && isAutoAccessorPropertyDeclaration(node) && !(node.flags & NodeFlags.Ambient)) {
5303353043
return grammarErrorOnNode(node.name, Diagnostics.Properties_with_the_accessor_modifier_are_only_available_when_targeting_ECMAScript_2015_and_higher);
5303453044
}
5303553045
if (isAutoAccessorPropertyDeclaration(node) && checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_accessor_property_cannot_be_declared_optional)) {
@@ -53178,7 +53188,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5317853188
const literalType = isLiteralTypeNode(node.parent) ||
5317953189
isPrefixUnaryExpression(node.parent) && isLiteralTypeNode(node.parent.parent);
5318053190
if (!literalType) {
53181-
if (languageVersion < ScriptTarget.ES2020) {
53191+
// Don't error on BigInt literals in ambient contexts
53192+
if (!(node.flags & NodeFlags.Ambient) && languageVersion < ScriptTarget.ES2020) {
5318253193
if (grammarErrorOnNode(node, Diagnostics.BigInt_literals_are_not_available_when_targeting_lower_than_ES2020)) {
5318353194
return true;
5318453195
}

src/compiler/parser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4564,6 +4564,7 @@ namespace Parser {
45644564
}
45654565
parseExpected(SyntaxKind.ColonToken);
45664566
attributes = parseImportAttributes(currentToken as SyntaxKind.WithKeyword | SyntaxKind.AssertKeyword, /*skipKeyword*/ true);
4567+
parseOptional(SyntaxKind.CommaToken);
45674568
if (!parseExpected(SyntaxKind.CloseBraceToken)) {
45684569
const lastError = lastOrUndefined(parseDiagnostics);
45694570
if (lastError && lastError.code === Diagnostics._0_expected.code) {

src/compiler/utilities.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7715,9 +7715,18 @@ export function base64decode(host: { base64decode?(input: string): string; } | u
77157715
export function readJsonOrUndefined(path: string, hostOrText: { readFile(fileName: string): string | undefined; } | string): object | undefined {
77167716
const jsonText = isString(hostOrText) ? hostOrText : hostOrText.readFile(path);
77177717
if (!jsonText) return undefined;
7718-
// gracefully handle if readFile fails or returns not JSON
7719-
const result = parseConfigFileTextToJson(path, jsonText);
7720-
return !result.error ? result.config : undefined;
7718+
// Try strictly parsing first, then fall back to our (slower)
7719+
// parser that is resilient to comments/trailing commas.
7720+
// package.json files should never have these, but we
7721+
// have no way to communicate these issues in the first place.
7722+
let result = tryParseJson(jsonText);
7723+
if (result === undefined) {
7724+
const looseResult = parseConfigFileTextToJson(path, jsonText);
7725+
if (!looseResult.error) {
7726+
result = looseResult.config;
7727+
}
7728+
}
7729+
return result;
77217730
}
77227731

77237732
/** @internal */
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
accessorInAmbientContextES5.ts(25,14): error TS18045: Properties with the 'accessor' modifier are only available when targeting ECMAScript 2015 and higher.
2+
3+
4+
==== accessorInAmbientContextES5.ts (1 errors) ====
5+
// Should allow accessor in ambient contexts even when targeting ES5
6+
7+
declare class AmbientClass {
8+
accessor prop1: string;
9+
static accessor prop2: number;
10+
private accessor prop3: boolean;
11+
private static accessor prop4: symbol;
12+
}
13+
14+
declare namespace AmbientNamespace {
15+
class C {
16+
accessor prop: string;
17+
}
18+
}
19+
20+
// Should also work in .d.ts files (simulated with declare)
21+
declare module "some-module" {
22+
export class ExportedClass {
23+
accessor value: any;
24+
}
25+
}
26+
27+
// Regular class should still error when targeting ES5
28+
class RegularClass {
29+
accessor shouldError: string; // Should still error
30+
~~~~~~~~~~~
31+
!!! error TS18045: Properties with the 'accessor' modifier are only available when targeting ECMAScript 2015 and higher.
32+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//// [tests/cases/compiler/accessorInAmbientContextES5.ts] ////
2+
3+
//// [accessorInAmbientContextES5.ts]
4+
// Should allow accessor in ambient contexts even when targeting ES5
5+
6+
declare class AmbientClass {
7+
accessor prop1: string;
8+
static accessor prop2: number;
9+
private accessor prop3: boolean;
10+
private static accessor prop4: symbol;
11+
}
12+
13+
declare namespace AmbientNamespace {
14+
class C {
15+
accessor prop: string;
16+
}
17+
}
18+
19+
// Should also work in .d.ts files (simulated with declare)
20+
declare module "some-module" {
21+
export class ExportedClass {
22+
accessor value: any;
23+
}
24+
}
25+
26+
// Regular class should still error when targeting ES5
27+
class RegularClass {
28+
accessor shouldError: string; // Should still error
29+
}
30+
31+
//// [accessorInAmbientContextES5.js]
32+
// Should allow accessor in ambient contexts even when targeting ES5
33+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
34+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
35+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
36+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
37+
};
38+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
39+
if (kind === "m") throw new TypeError("Private method is not writable");
40+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
41+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
42+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
43+
};
44+
var _RegularClass_shouldError_accessor_storage;
45+
// Regular class should still error when targeting ES5
46+
var RegularClass = /** @class */ (function () {
47+
function RegularClass() {
48+
_RegularClass_shouldError_accessor_storage.set(this, void 0);
49+
}
50+
Object.defineProperty(RegularClass.prototype, "shouldError", {
51+
get: function () { return __classPrivateFieldGet(this, _RegularClass_shouldError_accessor_storage, "f"); } // Should still error
52+
,
53+
set: function (value) { __classPrivateFieldSet(this, _RegularClass_shouldError_accessor_storage, value, "f"); },
54+
enumerable: false,
55+
configurable: true
56+
});
57+
return RegularClass;
58+
}());
59+
_RegularClass_shouldError_accessor_storage = new WeakMap();
60+
61+
62+
//// [accessorInAmbientContextES5.d.ts]
63+
declare class AmbientClass {
64+
accessor prop1: string;
65+
static accessor prop2: number;
66+
private accessor prop3;
67+
private static accessor prop4;
68+
}
69+
declare namespace AmbientNamespace {
70+
class C {
71+
accessor prop: string;
72+
}
73+
}
74+
declare module "some-module" {
75+
class ExportedClass {
76+
accessor value: any;
77+
}
78+
}
79+
declare class RegularClass {
80+
accessor shouldError: string;
81+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//// [tests/cases/compiler/accessorInAmbientContextES5.ts] ////
2+
3+
=== accessorInAmbientContextES5.ts ===
4+
// Should allow accessor in ambient contexts even when targeting ES5
5+
6+
declare class AmbientClass {
7+
>AmbientClass : Symbol(AmbientClass, Decl(accessorInAmbientContextES5.ts, 0, 0))
8+
9+
accessor prop1: string;
10+
>prop1 : Symbol(AmbientClass.prop1, Decl(accessorInAmbientContextES5.ts, 2, 28))
11+
12+
static accessor prop2: number;
13+
>prop2 : Symbol(AmbientClass.prop2, Decl(accessorInAmbientContextES5.ts, 3, 27))
14+
15+
private accessor prop3: boolean;
16+
>prop3 : Symbol(AmbientClass.prop3, Decl(accessorInAmbientContextES5.ts, 4, 34))
17+
18+
private static accessor prop4: symbol;
19+
>prop4 : Symbol(AmbientClass.prop4, Decl(accessorInAmbientContextES5.ts, 5, 36))
20+
}
21+
22+
declare namespace AmbientNamespace {
23+
>AmbientNamespace : Symbol(AmbientNamespace, Decl(accessorInAmbientContextES5.ts, 7, 1))
24+
25+
class C {
26+
>C : Symbol(C, Decl(accessorInAmbientContextES5.ts, 9, 36))
27+
28+
accessor prop: string;
29+
>prop : Symbol(C.prop, Decl(accessorInAmbientContextES5.ts, 10, 13))
30+
}
31+
}
32+
33+
// Should also work in .d.ts files (simulated with declare)
34+
declare module "some-module" {
35+
>"some-module" : Symbol("some-module", Decl(accessorInAmbientContextES5.ts, 13, 1))
36+
37+
export class ExportedClass {
38+
>ExportedClass : Symbol(ExportedClass, Decl(accessorInAmbientContextES5.ts, 16, 30))
39+
40+
accessor value: any;
41+
>value : Symbol(ExportedClass.value, Decl(accessorInAmbientContextES5.ts, 17, 32))
42+
}
43+
}
44+
45+
// Regular class should still error when targeting ES5
46+
class RegularClass {
47+
>RegularClass : Symbol(RegularClass, Decl(accessorInAmbientContextES5.ts, 20, 1))
48+
49+
accessor shouldError: string; // Should still error
50+
>shouldError : Symbol(RegularClass.shouldError, Decl(accessorInAmbientContextES5.ts, 23, 20))
51+
}

0 commit comments

Comments
 (0)