Skip to content

Commit 1eb83e5

Browse files
authored
build: update to TypeScript 5.0 (#26755)
Updates the repo to TypeScript 5 and resolves some errors that came up along the way. The most notable one is that none of the Codelyzer lint rules work anymore, because they depended upon deprecated APIs that have been deleted. I decided to reimplement the rule that asserts that lifecycle hook interfaces are present, but the remaining rules didn't seem that useful so I've removed them.
1 parent 5d41978 commit 1eb83e5

File tree

13 files changed

+1317
-1052
lines changed

13 files changed

+1317
-1052
lines changed

integration/yarn-pnp-compat/.yarn/releases/yarn-3.3.1.cjs

-823
This file was deleted.

integration/yarn-pnp-compat/.yarn/releases/yarn-3.4.1.cjs

+873
Large diffs are not rendered by default.
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
nodeLinker: pnp
22

3-
yarnPath: .yarn/releases/yarn-3.3.1.cjs
3+
yarnPath: .yarn/releases/yarn-3.4.1.cjs

integration/yarn-pnp-compat/BUILD.bazel

+8-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ load("//tools:defaults.bzl", "node_integration_test")
33
load("//tools:integration.bzl", "CLI_PROJECT_MAPPINGS")
44

55
npmPackageMappings = dicts.add(
6-
CLI_PROJECT_MAPPINGS,
6+
dicts.omit(
7+
CLI_PROJECT_MAPPINGS,
8+
# Exclude typescript, because Yarn PnP hasn't been updated to support versions 5.0+ yet.
9+
[
10+
"@npm//:typescript_archive",
11+
"typescript",
12+
],
13+
),
714
{
815
"//src/cdk:npm_package_archive": "@angular/cdk",
916
"//src/material:npm_package_archive": "@angular/material",

integration/yarn-pnp-compat/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"karma-coverage": "~2.1.0",
3838
"karma-jasmine": "~4.0.0",
3939
"karma-jasmine-html-reporter": "~1.7.0",
40-
"typescript": "file:../../node_modules/typescript"
40+
"typescript": "~4.9.3"
4141
},
42-
"packageManager": "yarn@3.3.1"
42+
"packageManager": "yarn@3.4.1"
4343
}

integration/yarn-pnp-compat/yarn.lock

+13-13
Original file line numberDiff line numberDiff line change
@@ -9696,23 +9696,23 @@ __metadata:
96969696
languageName: node
96979697
linkType: hard
96989698

9699-
"typescript@file:../../node_modules/typescript::locator=yarn-pnp-compat%40workspace%3A.":
9700-
version: 4.9.4
9701-
resolution: "typescript@file:../../node_modules/typescript#../../node_modules/typescript::hash=7421fa&locator=yarn-pnp-compat%40workspace%3A."
9699+
"typescript@npm:~4.9.3":
9700+
version: 4.9.5
9701+
resolution: "typescript@npm:4.9.5"
97029702
bin:
9703-
tsc: ./bin/tsc
9704-
tsserver: ./bin/tsserver
9705-
checksum: 37f6e2c3c5e2aa5934b85b0fddbf32eeac8b1bacf3a5b51d01946936d03f5377fe86255d4e5a4ae628fd0cd553386355ad362c57f13b4635064400f3e8e05b9d
9703+
tsc: bin/tsc
9704+
tsserver: bin/tsserver
9705+
checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db
97069706
languageName: node
97079707
linkType: hard
97089708

9709-
"typescript@patch:typescript@file%3A../../node_modules/typescript#~builtin<compat/typescript>":
9710-
version: 4.9.4
9711-
resolution: "typescript@patch:typescript@file%3A../../node_modules/typescript%23../../node_modules/typescript%3A%3Ahash=7421fa&locator=yarn-pnp-compat%2540workspace%253A.#~builtin<compat/typescript>::version=4.9.4&hash=ad5954"
9709+
"typescript@patch:typescript@~4.9.3#~builtin<compat/typescript>":
9710+
version: 4.9.5
9711+
resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin<compat/typescript>::version=4.9.5&hash=23ec76"
97129712
bin:
9713-
tsc: ./bin/tsc
9714-
tsserver: ./bin/tsserver
9715-
checksum: 1caaea6cb7f813e64345190fddc4e6c924d0b698ab81189b503763c4a18f7f5501c69362979d36e19c042d89d936443e768a78b0675690b35eb663d19e0eae71
9713+
tsc: bin/tsc
9714+
tsserver: bin/tsserver
9715+
checksum: ab417a2f398380c90a6cf5a5f74badd17866adf57f1165617d6a551f059c3ba0a3e4da0d147b3ac5681db9ac76a303c5876394b13b3de75fdd5b1eaa06181c9d
97169716
languageName: node
97179717
linkType: hard
97189718

@@ -10269,7 +10269,7 @@ __metadata:
1026910269
karma-jasmine-html-reporter: ~1.7.0
1027010270
rxjs: ~7.5.0
1027110271
tslib: ^2.3.0
10272-
typescript: "file:../../node_modules/typescript"
10272+
typescript: ~4.9.3
1027310273
zone.js: ~0.11.4
1027410274
languageName: unknown
1027510275
linkType: soft

package.json

+21-21
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@
5656
},
5757
"version": "16.0.0-next.1",
5858
"dependencies": {
59-
"@angular/animations": "^16.0.0-next.0",
60-
"@angular/common": "^16.0.0-next.0",
61-
"@angular/compiler": "^16.0.0-next.0",
62-
"@angular/core": "^16.0.0-next.0",
63-
"@angular/forms": "^16.0.0-next.0",
64-
"@angular/platform-browser": "^16.0.0-next.0",
59+
"@angular/animations": "^16.0.0-next.2",
60+
"@angular/common": "^16.0.0-next.2",
61+
"@angular/compiler": "^16.0.0-next.2",
62+
"@angular/core": "^16.0.0-next.2",
63+
"@angular/forms": "^16.0.0-next.2",
64+
"@angular/platform-browser": "^16.0.0-next.2",
6565
"@types/google.maps": "^3.47.3",
6666
"@types/youtube": "^0.0.46",
6767
"rxjs": "^6.6.7",
@@ -70,18 +70,18 @@
7070
"zone.js": "~0.11.5"
7171
},
7272
"devDependencies": {
73-
"@angular-devkit/build-angular": "^16.0.0-next.0",
74-
"@angular-devkit/core": "^16.0.0-next.0",
75-
"@angular-devkit/schematics": "^16.0.0-next.0",
73+
"@angular-devkit/build-angular": "^16.0.0-next.3",
74+
"@angular-devkit/core": "^16.0.0-next.3",
75+
"@angular-devkit/schematics": "^16.0.0-next.3",
7676
"@angular/bazel": "15.0.4",
7777
"@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#16f13b9e919478d61ec98ce60901f3bdebb5d4e5",
78-
"@angular/cli": "^16.0.0-next.0",
79-
"@angular/compiler-cli": "^16.0.0-next.0",
80-
"@angular/localize": "^16.0.0-next.0",
78+
"@angular/cli": "^16.0.0-next.3",
79+
"@angular/compiler-cli": "^16.0.0-next.2",
80+
"@angular/localize": "^16.0.0-next.2",
8181
"@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#46a6cb28a6ca6a3a7a096656280ed27b02243e9a",
82-
"@angular/platform-browser-dynamic": "^16.0.0-next.0",
83-
"@angular/platform-server": "^16.0.0-next.0",
84-
"@angular/router": "^16.0.0-next.0",
82+
"@angular/platform-browser-dynamic": "^16.0.0-next.2",
83+
"@angular/platform-server": "^16.0.0-next.2",
84+
"@angular/router": "^16.0.0-next.2",
8585
"@axe-core/webdriverjs": "^4.3.2",
8686
"@babel/core": "^7.16.12",
8787
"@bazel/bazelisk": "1.12.1",
@@ -146,7 +146,7 @@
146146
"@octokit/rest": "18.3.5",
147147
"@rollup/plugin-commonjs": "^21.0.0",
148148
"@rollup/plugin-node-resolve": "^13.1.3",
149-
"@schematics/angular": "^16.0.0-next.0",
149+
"@schematics/angular": "^16.0.0-next.3",
150150
"@types/babel__core": "^7.1.18",
151151
"@types/browser-sync": "^2.26.3",
152152
"@types/fs-extra": "^9.0.13",
@@ -165,7 +165,7 @@
165165
"autoprefixer": "^10.4.2",
166166
"browser-sync": "2.26.13",
167167
"chalk": "^4.1.0",
168-
"codelyzer": "^6.0.2",
168+
"cross-env": "^7.0.3",
169169
"date-fns": "^2.28.0",
170170
"dgeni": "^0.4.14",
171171
"dgeni-packages": "^0.29.5",
@@ -215,17 +215,17 @@
215215
"tsickle": "0.39.1",
216216
"tslint": "^6.1.3",
217217
"tsutils": "^3.21.0",
218-
"typescript": "~4.9.3",
218+
"typescript": "5.0.1-rc",
219219
"vrsource-tslint-rules": "6.0.0",
220220
"yaml": "^1.10.2",
221221
"yargs": "^17.3.1",
222222
"zx": "^6.2.4"
223223
},
224224
"resolutions": {
225-
"@angular/build-tooling/typescript": "~4.9.3",
226-
"@angular/ng-dev/typescript": "~4.9.3",
225+
"@angular/build-tooling/typescript": "5.0.1-rc",
226+
"@angular/ng-dev/typescript": "5.0.1-rc",
227227
"browser-sync-client": "2.26.13",
228-
"dgeni-packages/typescript": "~4.9.3",
228+
"dgeni-packages/typescript": "5.0.1-rc",
229229
"**/https-proxy-agent": "5.0.0"
230230
}
231231
}

src/material/form-field/form-field.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
[class.mdc-text-field--no-label]="!_hasFloatingLabel()"
4242
[class.mdc-text-field--disabled]="_control.disabled"
4343
[class.mdc-text-field--invalid]="_control.errorState"
44-
(click)="_control.onContainerClick && _control.onContainerClick($event)">
44+
(click)="_control.onContainerClick($event)">
4545
<div class="mat-mdc-form-field-focus-overlay" *ngIf="!_hasOutline() && !_control.disabled"></div>
4646
<div class="mat-mdc-form-field-flex">
4747
<div *ngIf="_hasOutline()" matFormFieldNotchedOutline

src/material/legacy-form-field/form-field.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="mat-form-field-wrapper">
22
<div class="mat-form-field-flex" #connectionContainer
3-
(click)="_control.onContainerClick && _control.onContainerClick($event)">
3+
(click)="_control.onContainerClick($event)">
44

55
<!-- Outline used for outline appearance. -->
66
<ng-container *ngIf="appearance == 'outline'">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import ts from 'typescript';
2+
import * as Lint from 'tslint';
3+
4+
const HOOKS_TO_INTERFACES: Record<string, string> = {
5+
'ngOnChanges': 'OnChanges',
6+
'ngOnInit': 'OnInit',
7+
'ngDoCheck': 'DoCheck',
8+
'ngAfterContentInit': 'AfterContentInit',
9+
'ngAfterContentChecked': 'AfterContentChecked',
10+
'ngAfterViewInit': 'AfterViewInit',
11+
'ngAfterViewChecked': 'AfterViewChecked',
12+
'ngOnDestroy': 'OnDestroy',
13+
'ngDoBootstrap': 'DoBootstrap',
14+
};
15+
16+
/**
17+
* Rule that requires classes using Angular lifecycle hooks to implement the appropriate interfaces.
18+
*/
19+
export class Rule extends Lint.Rules.AbstractRule {
20+
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
21+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
22+
}
23+
}
24+
25+
class Walker extends Lint.RuleWalker {
26+
override visitClassDeclaration(node: ts.ClassDeclaration) {
27+
for (const member of node.members) {
28+
if (
29+
!ts.isMethodDeclaration(member) ||
30+
!ts.isIdentifier(member.name) ||
31+
!HOOKS_TO_INTERFACES.hasOwnProperty(member.name.text)
32+
) {
33+
continue;
34+
}
35+
36+
const requiredInterface = HOOKS_TO_INTERFACES[member.name.text];
37+
const hasRequiredInterface = node.heritageClauses?.some(
38+
clause =>
39+
clause.token === ts.SyntaxKind.ImplementsKeyword &&
40+
clause.types.some(
41+
type =>
42+
ts.isExpressionWithTypeArguments(type) &&
43+
ts.isIdentifier(type.expression) &&
44+
type.expression.text === requiredInterface,
45+
),
46+
);
47+
48+
if (!hasRequiredInterface) {
49+
this.addFailureAtNode(
50+
node.name || node,
51+
`Class must implement interface ${requiredInterface}, because it uses Angular lifecycle hook ${member.name.text}`,
52+
);
53+
}
54+
}
55+
56+
return super.visitClassDeclaration(node);
57+
}
58+
}

tools/tslint-rules/memberNamingRule.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class Walker extends Lint.RuleWalker {
3737
override visitClassDeclaration(node: ts.ClassDeclaration) {
3838
node.members.forEach(member => {
3939
// Members without a modifier are considered public.
40-
if (!member.modifiers || this._hasModifier(member, ts.SyntaxKind.PublicKeyword)) {
40+
if (this._hasModifier(member, ts.SyntaxKind.PublicKeyword)) {
4141
this._validateMember(member, 'public');
4242
} else if (this._hasModifier(member, ts.SyntaxKind.PrivateKeyword)) {
4343
this._validateMember(member, 'private');
@@ -105,6 +105,6 @@ class Walker extends Lint.RuleWalker {
105105
node: ts.ClassElement | ts.ParameterDeclaration,
106106
targetKind: ts.SyntaxKind,
107107
): boolean {
108-
return !!node.modifiers?.some(({kind}) => kind === targetKind);
108+
return ts.canHaveModifiers(node) && !!node.modifiers?.some(({kind}) => kind === targetKind);
109109
}
110110
}

tslint.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
{
22
"extends": ["rxjs-tslint-rules"],
3-
"rulesDirectory": [
4-
"./tools/tslint-rules/",
5-
"node_modules/vrsource-tslint-rules/rules",
6-
"node_modules/codelyzer"
7-
],
3+
"rulesDirectory": ["./tools/tslint-rules/", "node_modules/vrsource-tslint-rules/rules"],
84
"rules": {
95
"ban-types": [true, ["ReadonlyArray<.+>", "Use 'readonly T[]' instead."]],
106
// Disable this flag because of SHA tslint#48b0c597f9257712c7d1f04b55ed0aa60e333f6a
@@ -58,15 +54,6 @@
5854
"await-promise": [true, "PromiseLike"],
5955
"array-type": [true, "array"],
6056

61-
// Codelyzer
62-
"template-banana-in-box": true,
63-
"contextual-lifecycle": true,
64-
"contextual-decorator": true,
65-
"no-output-on-prefix": true,
66-
"prefer-output-readonly": true,
67-
"template-no-negated-async": true,
68-
"use-lifecycle-interface": true,
69-
7057
// RxJS
7158
"rxjs-no-unsafe-takeuntil": true,
7259
"rxjs-no-unsafe-catch": true,
@@ -80,6 +67,7 @@
8067
"no-undecorated-class-with-angular-features": true,
8168
"setters-after-getters": true,
8269
"ng-on-changes-property-access": true,
70+
"lifecycle-hook-interface": true,
8371
"require-breaking-change-version": true,
8472
"no-nested-ternary": true,
8573
"prefer-const-enum": true,

0 commit comments

Comments
 (0)