Skip to content

fix(46246): Extending Class/Interface With a Primitive Throws a More Concise Error Report #49208

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

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ gulp help # List the above commands.
## Usage

```bash
node built/local/tsc.js hello.ts
node <repo-root>/built/local/tsc.js hello.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you revert the changes here and in package.json? They're not related to the fix.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, will do.

```


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"fancy-log": "latest",
"fs-extra": "^9.0.0",
"glob": "latest",
"gulp": "^4.0.0",
"gulp": "^4.0.2",
"gulp-concat": "latest",
"gulp-insert": "latest",
"gulp-newer": "latest",
Expand Down
17 changes: 17 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2180,6 +2180,7 @@ namespace ts {
!checkAndReportErrorForExtendingInterface(errorLocation) &&
!checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
!checkAndReportErrorForExportingPrimitiveType(errorLocation, name) &&
!checkAndReportErrorForExtendingPrimitiveType(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) {
Expand Down Expand Up @@ -2497,6 +2498,22 @@ namespace ts {
return false;
}

function checkAndReportErrorForExtendingPrimitiveType(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) {
if (isPrimitiveTypeName(name) && !(errorLocation.parent.parent.kind & ~SyntaxKind.HeritageClause)) {
const rawName = unescapeLeadingUnderscores(name);
if(meaning & SymbolFlags.Interface) {
error(errorLocation, Diagnostics.An_interface_cannot_extend_a_primitive_type_like_0_An_interface_can_only_extend_named_object_types, rawName);
}
else if(meaning & SymbolFlags.Class) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A syntactic check (of errorLocation.parent.parent.parent I think) is better. meaning is the kind of symbol requested by name resolution.

Also you should probably check that all those parent nodes are defined.

error(errorLocation, Diagnostics.A_class_cannot_extend_a_primitive_type_like_0_A_class_can_only_extend_named_object_types, rawName);
}
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you should return true unless one of the above errors was issued.

Copy link
Author

@maross3 maross3 Jun 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 2503 if (isPrimitiveTypeName(name) && !(errorLocation.parent.parent.kind & ~SyntaxKind.HeritageClause))

This is our error check. If we are in this scope, there is an error, so we only need one return statement. The if/else checks if it's a class or interface and throws a respective error. After the error is called, we return true. Would you like to see the return within the inner scopes above, or is this okay? (It does need a default error, though)

}
}
return false;
}

function maybeMappedType(node: Node, symbol: Symbol) {
const container = findAncestor(node.parent, n =>
isComputedPropertyName(n) || isPropertySignature(n) ? false : isTypeLiteralNode(n) || "quit") as TypeLiteralNode | undefined;
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -7356,5 +7356,13 @@
"A 'return' statement cannot be used inside a class static block.": {
"category": "Error",
"code": 18041
},
"An interface cannot extend a primitive type like '{0}'. An interface can only extend named object types.": {
"category": "Error",
"code": 18042
},
"A class cannot extend a primitive type like '{0}'. A class can only extend named object types.": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're adding class-specific errors, a separate error for when classes implement primitive types would be nice.

"category": "Error",
"code": 18043
}
}
12 changes: 6 additions & 6 deletions tests/baselines/reference/classExtendingPrimitive.errors.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(3,17): error TS2693: 'number' only refers to a type, but is being used as a value here.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(4,18): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(5,18): error TS2693: 'boolean' only refers to a type, but is being used as a value here.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(3,17): error TS18043: A class cannot extend a primitive type like 'number'. A class can only extend named object types.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(4,18): error TS18043: A class cannot extend a primitive type like 'string'. A class can only extend named object types.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(5,18): error TS18043: A class cannot extend a primitive type like 'boolean'. A class can only extend named object types.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(6,18): error TS2304: Cannot find name 'Void'.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(7,19): error TS1109: Expression expected.
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingPrimitive.ts(8,18): error TS2304: Cannot find name 'Null'.
Expand All @@ -14,13 +14,13 @@ tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/cla

class C extends number { }
~~~~~~
!!! error TS2693: 'number' only refers to a type, but is being used as a value here.
!!! error TS18043: A class cannot extend a primitive type like 'number'. A class can only extend named object types.
class C2 extends string { }
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.
!!! error TS18043: A class cannot extend a primitive type like 'string'. A class can only extend named object types.
class C3 extends boolean { }
~~~~~~~
!!! error TS2693: 'boolean' only refers to a type, but is being used as a value here.
!!! error TS18043: A class cannot extend a primitive type like 'boolean'. A class can only extend named object types.
class C4 extends Void { }
~~~~
!!! error TS2304: Cannot find name 'Void'.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
tests/cases/compiler/errorLocationForInterfaceExtension.ts(3,21): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/compiler/errorLocationForInterfaceExtension.ts(3,21): error TS18042: An interface cannot extend a primitive type like 'string'. An interface can only extend named object types.


==== tests/cases/compiler/errorLocationForInterfaceExtension.ts (1 errors) ====
var n = '';

interface x extends string { }
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.
!!! error TS18042: An interface cannot extend a primitive type like 'string'. An interface can only extend named object types.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(8,14): error TS
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(8,16): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,17): error TS1109: Expression expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,22): error TS1109: Expression expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,23): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,23): error TS18043: A class cannot extend a primitive type like 'string'. A class can only extend named object types.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,29): error TS1005: ',' expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(15,8): error TS1109: Expression expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(18,2): error TS1109: Expression expected.
Expand Down Expand Up @@ -49,7 +49,7 @@ tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(42,20): error T
~
!!! error TS1109: Expression expected.
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.
!!! error TS18043: A class cannot extend a primitive type like 'string'. A class can only extend named object types.
~
!!! error TS1005: ',' expected.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(8,14): error TS
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(8,16): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,17): error TS1109: Expression expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,22): error TS1109: Expression expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,23): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,23): error TS18043: A class cannot extend a primitive type like 'string'. A class can only extend named object types.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(11,29): error TS1005: ',' expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(15,8): error TS1109: Expression expected.
tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(18,2): error TS1109: Expression expected.
Expand Down Expand Up @@ -49,7 +49,7 @@ tests/cases/conformance/externalModules/topLevelAwaitErrors.1.ts(42,20): error T
~
!!! error TS1109: Expression expected.
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.
!!! error TS18043: A class cannot extend a primitive type like 'string'. A class can only extend named object types.
~
!!! error TS1005: ',' expected.
}
Expand Down