Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25174,6 +25174,10 @@ namespace ts {
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
}
propType = indexInfo.type;

if (compilerOptions.pedanticPropertyLookup && isPropertyAccessExpression(node)) {
error(node, Diagnostics.Access_property_0_by_index_signature_is_disallowed, unescapeLeadingUnderscores(right.escapedText));
}
}
else {
if (prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) {
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,15 @@ namespace ts {
category: Diagnostics.Additional_Checks,
description: Diagnostics.Report_errors_for_fallthrough_cases_in_switch_statement
},
{
name: "pedanticPropertyLookup",
type: "boolean",
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
showInSimplifiedHelpView: false,
category: Diagnostics.Additional_Checks,
description: Diagnostics.Force_access_member_from_index_signature_with_element_access_expression
},

// Module Resolution
{
Expand Down
16 changes: 16 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3386,6 +3386,10 @@
"category": "Error",
"code": 4110
},
"Access property '{0}' by index signature is disallowed.": {
Comment thread
Kingwl marked this conversation as resolved.
Outdated
"category": "Error",
"code": 4111
},

"The current host does not support the '{0}' option.": {
"category": "Error",
Expand Down Expand Up @@ -4686,6 +4690,10 @@
"category": "Error",
"code": 6504
},
"Force access member from index signature with element access expression.": {
Comment thread
Kingwl marked this conversation as resolved.
Outdated
"category": "Error",
"code": 6803
},

"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
Expand Down Expand Up @@ -5876,6 +5884,14 @@
"category": "Message",
"code": 95142
},
"Use element access for '{0}'": {
"category": "Message",
"code": 95143
},
"Use element access for all property access": {
Comment thread
Kingwl marked this conversation as resolved.
Outdated
"category": "Message",
"code": 95144
},

"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5714,6 +5714,7 @@ namespace ts {
outDir?: string;
outFile?: string;
paths?: MapLike<string[]>;
pedanticPropertyLookup?: boolean;
/*@internal*/ plugins?: PluginImport[];
preserveConstEnums?: boolean;
preserveSymlinks?: boolean;
Expand Down
35 changes: 35 additions & 0 deletions src/services/codefixes/fixPedanticPropertyLookup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* @internal */
namespace ts.codefix {
const fixId = "fixPedanticPropertyLookup";
const errorCodes = [
Diagnostics.Access_property_0_by_index_signature_is_disallowed.code
];

registerCodeFix({
errorCodes,
fixIds: [fixId],
getCodeActions(context) {
const { sourceFile, span } = context;
const property = getPropertyAccessExpression(sourceFile, span.start);
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, property));
return [createCodeFixAction(fixId, changes, [Diagnostics.Use_element_access_for_0, property.name.text], fixId, Diagnostics.Use_element_access_for_all_property_access)];
},
getAllCodeActions: context =>
codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, getPropertyAccessExpression(diag.file, diag.start)))
});

function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: PropertyAccessExpression): void {
const argumentsExpression = factory.createStringLiteral(node.name.text);
changes.replaceNode(
sourceFile,
node,
isPropertyAccessChain(node) ?
factory.createElementAccessChain(node.expression, node.questionDotToken, argumentsExpression) :
factory.createElementAccessExpression(node.expression, argumentsExpression)
);
}

function getPropertyAccessExpression(sourceFile: SourceFile, pos: number): PropertyAccessExpression {
return cast(getTokenAtPosition(sourceFile, pos).parent, isPropertyAccessExpression);
}
}
1 change: 1 addition & 0 deletions src/services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"codefixes/convertToTypeOnlyImport.ts",
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
"codefixes/importFixes.ts",
"codefixes/fixPedanticPropertyLookup.ts",
"codefixes/fixImplicitThis.ts",
"codefixes/fixIncorrectNamedTupleSyntax.ts",
"codefixes/fixSpelling.ts",
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2800,6 +2800,7 @@ declare namespace ts {
outDir?: string;
outFile?: string;
paths?: MapLike<string[]>;
pedanticPropertyLookup?: boolean;
preserveConstEnums?: boolean;
preserveSymlinks?: boolean;
project?: string;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2800,6 +2800,7 @@ declare namespace ts {
outDir?: string;
outFile?: string;
paths?: MapLike<string[]>;
pedanticPropertyLookup?: boolean;
preserveConstEnums?: boolean;
preserveSymlinks?: boolean;
project?: string;
Expand Down
54 changes: 54 additions & 0 deletions tests/baselines/reference/propertyLookup1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
tests/cases/conformance/pedantics/propertyLookup1.ts(24,1): error TS4111: Access property 'foo' by index signature is disallowed.
tests/cases/conformance/pedantics/propertyLookup1.ts(32,1): error TS4111: Access property 'bar' by index signature is disallowed.
tests/cases/conformance/pedantics/propertyLookup1.ts(40,1): error TS4111: Access property 'bar' by index signature is disallowed.


==== tests/cases/conformance/pedantics/propertyLookup1.ts (3 errors) ====
interface A {
foo: string
}

interface B {
[k: string]: string
}

interface C {
foo: string
[k: string]: string
}

declare const a: A;
declare const b: B;
declare const c: C;
declare const d: C | undefined;

// access property
a.foo;
a["foo"]

// access index signature
b.foo;
~~~~~
!!! error TS4111: Access property 'foo' by index signature is disallowed.
b["foo"];

// access property
c.foo;
c["foo"]

// access index signature
c.bar;
~~~~~
!!! error TS4111: Access property 'bar' by index signature is disallowed.
c["bar"];

// optional access property
d?.foo;
d?.["foo"]

// optional access index signature
d?.bar;
~~~~~~
!!! error TS4111: Access property 'bar' by index signature is disallowed.
d?.["bar"];

63 changes: 63 additions & 0 deletions tests/baselines/reference/propertyLookup1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//// [propertyLookup1.ts]
interface A {
foo: string
}

interface B {
[k: string]: string
}

interface C {
foo: string
[k: string]: string
}

declare const a: A;
declare const b: B;
declare const c: C;
declare const d: C | undefined;

// access property
a.foo;
a["foo"]

// access index signature
b.foo;
b["foo"];

// access property
c.foo;
c["foo"]

// access index signature
c.bar;
c["bar"];

// optional access property
d?.foo;
d?.["foo"]

// optional access index signature
d?.bar;
d?.["bar"];


//// [propertyLookup1.js]
// access property
a.foo;
a["foo"];
// access index signature
b.foo;
b["foo"];
// access property
c.foo;
c["foo"];
// access index signature
c.bar;
c["bar"];
// optional access property
d === null || d === void 0 ? void 0 : d.foo;
d === null || d === void 0 ? void 0 : d["foo"];
// optional access index signature
d === null || d === void 0 ? void 0 : d.bar;
d === null || d === void 0 ? void 0 : d["bar"];
92 changes: 92 additions & 0 deletions tests/baselines/reference/propertyLookup1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
=== tests/cases/conformance/pedantics/propertyLookup1.ts ===
interface A {
>A : Symbol(A, Decl(propertyLookup1.ts, 0, 0))

foo: string
>foo : Symbol(A.foo, Decl(propertyLookup1.ts, 0, 13))
}

interface B {
>B : Symbol(B, Decl(propertyLookup1.ts, 2, 1))

[k: string]: string
>k : Symbol(k, Decl(propertyLookup1.ts, 5, 5))
}

interface C {
>C : Symbol(C, Decl(propertyLookup1.ts, 6, 1))

foo: string
>foo : Symbol(C.foo, Decl(propertyLookup1.ts, 8, 13))

[k: string]: string
>k : Symbol(k, Decl(propertyLookup1.ts, 10, 5))
}

declare const a: A;
>a : Symbol(a, Decl(propertyLookup1.ts, 13, 13))
>A : Symbol(A, Decl(propertyLookup1.ts, 0, 0))

declare const b: B;
>b : Symbol(b, Decl(propertyLookup1.ts, 14, 13))
>B : Symbol(B, Decl(propertyLookup1.ts, 2, 1))

declare const c: C;
>c : Symbol(c, Decl(propertyLookup1.ts, 15, 13))
>C : Symbol(C, Decl(propertyLookup1.ts, 6, 1))

declare const d: C | undefined;
>d : Symbol(d, Decl(propertyLookup1.ts, 16, 13))
>C : Symbol(C, Decl(propertyLookup1.ts, 6, 1))

// access property
a.foo;
>a.foo : Symbol(A.foo, Decl(propertyLookup1.ts, 0, 13))
>a : Symbol(a, Decl(propertyLookup1.ts, 13, 13))
>foo : Symbol(A.foo, Decl(propertyLookup1.ts, 0, 13))

a["foo"]
>a : Symbol(a, Decl(propertyLookup1.ts, 13, 13))
>"foo" : Symbol(A.foo, Decl(propertyLookup1.ts, 0, 13))

// access index signature
b.foo;
>b : Symbol(b, Decl(propertyLookup1.ts, 14, 13))

b["foo"];
>b : Symbol(b, Decl(propertyLookup1.ts, 14, 13))

// access property
c.foo;
>c.foo : Symbol(C.foo, Decl(propertyLookup1.ts, 8, 13))
>c : Symbol(c, Decl(propertyLookup1.ts, 15, 13))
>foo : Symbol(C.foo, Decl(propertyLookup1.ts, 8, 13))

c["foo"]
>c : Symbol(c, Decl(propertyLookup1.ts, 15, 13))
>"foo" : Symbol(C.foo, Decl(propertyLookup1.ts, 8, 13))

// access index signature
c.bar;
>c : Symbol(c, Decl(propertyLookup1.ts, 15, 13))

c["bar"];
>c : Symbol(c, Decl(propertyLookup1.ts, 15, 13))

// optional access property
d?.foo;
>d?.foo : Symbol(C.foo, Decl(propertyLookup1.ts, 8, 13))
>d : Symbol(d, Decl(propertyLookup1.ts, 16, 13))
>foo : Symbol(C.foo, Decl(propertyLookup1.ts, 8, 13))

d?.["foo"]
>d : Symbol(d, Decl(propertyLookup1.ts, 16, 13))
>"foo" : Symbol(C.foo, Decl(propertyLookup1.ts, 8, 13))

// optional access index signature
d?.bar;
>d : Symbol(d, Decl(propertyLookup1.ts, 16, 13))

d?.["bar"];
>d : Symbol(d, Decl(propertyLookup1.ts, 16, 13))

Loading