-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Fix many no-object-literal-type-assertion lint errors #17278
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
Conversation
src/compiler/binder.ts
Outdated
clauseEnd, | ||
antecedent | ||
}; | ||
return id<FlowSwitchClause>({ flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this better? You're now hoping this gets inlined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better from a type perspective, because type checking within a cast is weaker than assignability type checking. (See https://palantir.github.io/tslint/rules/no-object-literal-type-assertion/) If you want I could add a local variable instead of using id
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these methods, I'd just change the return type of each create
function to return the more specific kind of FlowNode
it actually creates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with Wesley; best solution is to create a closed set of control-flow nodes and change the return type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those functions in binder.ts
don't actually always return the specific type they're named after.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could invert FlowNode
into a union of its current subtypes.
26977a7
to
31f4930
Compare
31f4930
to
c0e4a12
Compare
@@ -444,10 +444,10 @@ namespace ts.server { | |||
if (!this.eventHandler) { | |||
return; | |||
} | |||
this.eventHandler(<ProjectLanguageServiceStateEvent>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The casts in this file aren't required at all if you change ProjectServiceEventHandler
to use overloads instead of a single overload with a union for its parameter type. That's probably a nicer solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing the type of ProjectServiceEventHandler
would require changing some other things; I want to avoid that in this PR. How about just a local variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or we could add a union, but a local is better than the function call.
@@ -352,14 +352,14 @@ namespace ts.server.typingsInstaller { | |||
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles))); | |||
} | |||
finally { | |||
this.sendResponse(<EndInstallTypes>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, actually. An overloaded signature will provide a better behavior than the union here.
@@ -935,25 +935,25 @@ namespace ts.projectSystem { | |||
import * as cmd from "commander | |||
` | |||
}; | |||
session.executeCommand(<server.protocol.OpenRequest>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The casts in this file would be unneeded by using the overloaded signature, too.
src/harness/unittests/session.ts
Outdated
@@ -93,14 +93,14 @@ namespace ts.server { | |||
|
|||
session.executeCommand(req); | |||
|
|||
expect(lastSent).to.deep.equal(<protocol.Response>{ | |||
expect(lastSent).to.deep.equal(id<protocol.Response>({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, this is a failing of the chai type definition. I expect expect
should capture an generic to expect as the argument for equal
, which would allow you to cast the type under expect
and receive proper completions in the equal
. Failing that, this being a unit test, if we're removing id
, a variable assignment is fine.
start: undefined, | ||
}; | ||
}); | ||
expected.errors = expected.errors.map<Diagnostic>(error => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My personal preference is to annotate the return type on the lambda ((error): Diagnostic => ...
), rather than specifying the generic argument. Specifying generic arguments always feels a tad clunky to me. No big deal either way, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've always felt specifying the return type on the lambda looked clunky :). It's too bad we don't use contextual typing here, since expected.errors
has a definite type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preference for the return type stems from being able to refactor generics into or out of the function signature without affecting the consuming code. But I do agree - appropriately contextually typing the object would be best.
@@ -518,18 +518,18 @@ namespace ts.projectSystem { | |||
}; | |||
const host = createServerHost([f], { newLine }); | |||
const session = createSession(host); | |||
session.executeCommand(<server.protocol.OpenRequest>{ | |||
session.executeCommand(id<server.protocol.OpenRequest>({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, these id
calls are unnecessary if we swap to an overloaded signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or a union
src/harness/projectsRunner.ts
Outdated
@@ -426,12 +426,12 @@ class ProjectRunner extends RunnerBase { | |||
compilerResult.program ? | |||
ts.filter(compilerResult.program.getSourceFiles(), sourceFile => !Harness.isDefaultLibraryFile(sourceFile.fileName)) : | |||
[]), | |||
sourceFile => <Harness.Compiler.TestFile>{ | |||
sourceFile => ts.id<Harness.Compiler.TestFile>({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(sourceFile): Harness.Compiler.TestFile
?
src/compiler/checker.ts
Outdated
@@ -6254,16 +6254,16 @@ namespace ts { | |||
const parameterName = node.parameterName as Identifier; | |||
return { | |||
kind: TypePredicateKind.Identifier, | |||
parameterName: parameterName ? parameterName.text : undefined, | |||
parameterName: parameterName ? <string>parameterName.text : undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cast is unsafe and probably indicative of a bug somewhere. Something to do with underscore prefixed identifiers and type guards. Use unescapeLeadingUnderscores(parameterName.text)
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch; intended to leave any such changes out of this PR, so I'll revert it.
src/compiler/binder.ts
Outdated
clauseEnd, | ||
antecedent | ||
}; | ||
return id<FlowSwitchClause>({ flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these methods, I'd just change the return type of each create
function to return the more specific kind of FlowNode
it actually creates.
Don't know why GitHub won't let me respond to the last comment, but those functions in |
7bb6c18
to
b720e0f
Compare
b720e0f
to
08d8e04
Compare
@DanielRosenwasser Those functions in |
@DanielRosenwasser @weswigham Bump -- see above comments about |
src/compiler/emitter.ts
Outdated
@@ -1195,7 +1195,7 @@ namespace ts { | |||
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { | |||
const dotRangeStart = node.expression.end; | |||
const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1; | |||
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; | |||
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; // tslint:disable-line no-object-literal-type-assertion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be changed into:
const dotToken = createToken(SyntaxKind.DotToken);
dotToken.pos = dotRangeStart;
dotToken.end = dotRangeEnd;
@@ -518,18 +518,18 @@ namespace ts.projectSystem { | |||
}; | |||
const host = createServerHost([f], { newLine }); | |||
const session = createSession(host); | |||
session.executeCommand(<server.protocol.OpenRequest>{ | |||
session.executeCommand(id<server.protocol.OpenRequest>({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or a union
src/harness/unittests/matchFiles.ts
Outdated
start: undefined, | ||
}; | ||
}); | ||
expected.errors = expected.errors.map((error): Diagnostic => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected.errors = expected.errors.map<Diagnostic>(error => ({
?
@@ -444,10 +444,10 @@ namespace ts.server { | |||
if (!this.eventHandler) { | |||
return; | |||
} | |||
this.eventHandler(<ProjectLanguageServiceStateEvent>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or we could add a union, but a local is better than the function call.
src/server/session.ts
Outdated
private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: Diagnostic[]) { | ||
return diagnostics.map(d => <protocol.DiagnosticWithLinePosition>{ | ||
private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: Diagnostic[]): protocol.DiagnosticWithLinePosition[] { | ||
return diagnostics.map(d => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
diagnostics.map<protocol.DiagnosticWithLinePosition>(d => ({
?
@@ -2015,7 +2015,7 @@ namespace ts { | |||
* | |||
* @param block Information about the block. | |||
*/ | |||
function beginBlock(block: CodeBlock): number { | |||
function beginBlock<T extends CodeBlock>(block: T): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would also end up better (no casts, no generics) if CodeBlock became a union?
fb6c2fd
to
f6a2ea6
Compare
src/compiler/emitter.ts
Outdated
@@ -1195,7 +1195,9 @@ namespace ts { | |||
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { | |||
const dotRangeStart = node.expression.end; | |||
const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1; | |||
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; // tslint:disable-line no-object-literal-type-assertion | |||
const dotToken = createToken(SyntaxKind.DotToken); | |||
dotToken.pos = dotRangeStart; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably inline the locals dotRangeStart
and dotRangeEnd
as they are fairly trivial.
start: undefined, | ||
}; | ||
}); | ||
expected.errors = expected.errors.map<Diagnostic>(error => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've always felt specifying the return type on the lambda looked clunky :). It's too bad we don't use contextual typing here, since expected.errors
has a definite type.
@@ -164,12 +164,13 @@ namespace ts { | |||
} | |||
|
|||
// A generated code block | |||
interface CodeBlock { | |||
type CodeBlock = | ExceptionBlock | LabeledBlock | SwitchBlock | LoopBlock | WithBlock; | |||
interface CodeBlockBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just move kind
to each code block with its more specific type and remove this type. That way we can just leverage kind
as a discriminant.
Tested enabling the
no-object-literal-type-assertion
lint rule.This doesn't fix every case; the remaining cases will be harder to fix, and a few may be genuine bugs.
There were only a few categories:
id<T>({ ... })
.