fix use-before-init error when targeting ES2022 (#55028)

This commit is contained in:
Zzzen
2023-07-28 16:02:09 -07:00
committed by GitHub
parent 6037cf5db8
commit 23eabea067
25 changed files with 824 additions and 19 deletions
+13 -11
View File
@@ -274,6 +274,7 @@ import {
getEmitModuleKind,
getEmitModuleResolutionKind,
getEmitScriptTarget,
getEmitStandardClassFields,
getEnclosingBlockScopeContainer,
getEnclosingContainer,
getEntityNameFromTypeNode,
@@ -1437,6 +1438,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
var moduleKind = getEmitModuleKind(compilerOptions);
var legacyDecorators = !!compilerOptions.experimentalDecorators;
var useDefineForClassFields = getUseDefineForClassFields(compilerOptions);
var emitStandardClassFields = getEmitStandardClassFields(compilerOptions);
var allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
var strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
var strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
@@ -2785,8 +2787,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ false);
}
else if (isParameterPropertyDeclaration(declaration, declaration.parent)) {
// foo = this.bar is illegal in esnext+useDefineForClassFields when bar is a parameter property
return !(getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields
// foo = this.bar is illegal in emitStandardClassFields when bar is a parameter property
return !(emitStandardClassFields
&& getContainingClass(declaration) === getContainingClass(usage)
&& isUsedInFunctionOrInstanceProperty(usage, declaration));
}
@@ -2798,7 +2800,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// 1. inside an export specifier
// 2. inside a function
// 3. inside an instance property initializer, a reference to a non-instance property
// (except when target: "esnext" and useDefineForClassFields: true and the reference is to a parameter property)
// (except when emitStandardClassFields: true and the reference is to a parameter property)
// 4. inside a static property initializer, a reference to a static method in the same class
// 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ)
// or if usage is in a type context:
@@ -2817,7 +2819,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return true;
}
if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
if (getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022 && useDefineForClassFields
if (emitStandardClassFields
&& getContainingClass(declaration)
&& (isPropertyDeclaration(declaration) || isParameterPropertyDeclaration(declaration, declaration.parent))) {
return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ true);
@@ -2974,7 +2976,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
case SyntaxKind.PropertyDeclaration:
// static properties in classes introduce temporary variables
if (hasStaticModifier(node)) {
return target < ScriptTarget.ESNext || !useDefineForClassFields;
return !emitStandardClassFields;
}
return requiresScopeChangeWorker((node as PropertyDeclaration).name);
default:
@@ -3392,10 +3394,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// 1. When result is undefined, after checking for a missing "this."
// 2. When result is defined
function checkAndReportErrorForInvalidInitializer() {
if (propertyWithInvalidInitializer && !(useDefineForClassFields && getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022)) {
if (propertyWithInvalidInitializer && !emitStandardClassFields) {
// We have a match, but the reference occurred within a property initializer and the identifier also binds
// to a local variable in the constructor where the code will be emitted. Note that this is actually allowed
// with ESNext+useDefineForClassFields because the scope semantics are different.
// with emitStandardClassFields because the scope semantics are different.
error(errorLocation,
errorLocation && propertyWithInvalidInitializer.type && textRangeContainsPositionInclusive(propertyWithInvalidInitializer.type, errorLocation.pos)
? Diagnostics.Type_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor
@@ -31739,7 +31741,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
&& !(isAccessExpression(node) && isAccessExpression(node.expression))
&& !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
&& !(isMethodDeclaration(valueDeclaration) && getCombinedModifierFlagsCached(valueDeclaration) & ModifierFlags.Static)
&& (compilerOptions.useDefineForClassFields || !isPropertyDeclaredInAncestorClass(prop))) {
&& (useDefineForClassFields || !isPropertyDeclaredInAncestorClass(prop))) {
diagnosticMessage = error(right, Diagnostics.Property_0_is_used_before_its_initialization, declarationName);
}
else if (valueDeclaration.kind === SyntaxKind.ClassDeclaration &&
@@ -38453,7 +38455,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
case "length":
case "caller":
case "arguments":
if (compilerOptions.useDefineForClassFields) {
if (useDefineForClassFields) {
break;
}
// fall through
@@ -38658,7 +38660,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// or the containing class declares instance member variables with initializers.
const superCallShouldBeRootLevel =
(getEmitScriptTarget(compilerOptions) !== ScriptTarget.ESNext || !useDefineForClassFields) &&
!emitStandardClassFields &&
(some((node.parent as ClassDeclaration).members, isInstancePropertyWithInitializerOrPrivateIdentifierProperty) ||
some(node.parameters, p => hasSyntacticModifier(p, ModifierFlags.ParameterPropertyModifier)));
@@ -42935,7 +42937,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
!legacyDecorators && languageVersion < ScriptTarget.ESNext &&
classOrConstructorParameterIsDecorated(/*useLegacyDecorators*/ false, node);
const willTransformPrivateElementsOrClassStaticBlocks = languageVersion <= ScriptTarget.ES2022;
const willTransformInitializers = !useDefineForClassFields || languageVersion < ScriptTarget.ES2022;
const willTransformInitializers = !emitStandardClassFields;
if (willTransformStaticElementsOfDecoratedClass || willTransformPrivateElementsOrClassStaticBlocks) {
for (const member of node.members) {
if (willTransformStaticElementsOfDecoratedClass && classElementOrClassElementParameterIsDecorated(/*useLegacyDecorators*/ false, member, node)) {
+5
View File
@@ -8666,6 +8666,11 @@ export function getUseDefineForClassFields(compilerOptions: CompilerOptions): bo
return compilerOptions.useDefineForClassFields === undefined ? getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022 : compilerOptions.useDefineForClassFields;
}
/** @internal */
export function getEmitStandardClassFields(compilerOptions: CompilerOptions) {
return compilerOptions.useDefineForClassFields !== false && getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022;
}
/** @internal */
export function compilerOptionsAffectSemanticDiagnostics(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean {
return optionsHaveChanges(oldOptions, newOptions, semanticDiagnosticsOptionDeclarations);