Skip to content

Commit ab811f7

Browse files
committed
no-unnecessary-type-annotation: make signature parsing more reliable
Fixes: #57
1 parent 6ed1c23 commit ab811f7

File tree

2 files changed

+19
-14
lines changed

2 files changed

+19
-14
lines changed

rules/noUnnecessaryTypeAnnotationRule.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ export class Rule extends Lint.Rules.TypedRule {
3535
}
3636
}
3737

38+
const formatFlags = ts.TypeFormatFlags.UseStructuralFallback
39+
| ts.TypeFormatFlags.UseFullyQualifiedType
40+
| ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope
41+
| ts.TypeFormatFlags.NoTruncation
42+
| ts.TypeFormatFlags.WriteClassExpressionAsTypeLiteral
43+
| ts.TypeFormatFlags.WriteArrowStyleSignature;
44+
3845
function walk(ctx: Lint.WalkContext<IOptions>, checker: ts.TypeChecker) {
3946
return ts.forEachChild(ctx.sourceFile, function cb(node): void {
4047
switch (node.kind) {
@@ -190,7 +197,7 @@ function walk(ctx: Lint.WalkContext<IOptions>, checker: ts.TypeChecker) {
190197

191198
// TODO this could use a little more effort
192199
function typesAreEqual(a: ts.Type, b: ts.Type): boolean {
193-
return a === b || checker.typeToString(a) === checker.typeToString(b);
200+
return a === b || checker.typeToString(a, undefined, formatFlags) === checker.typeToString(b, undefined, formatFlags);
194201
}
195202

196203
function getContextualTypeOfFunction(func: FunctionExpressionLike): ts.Type | undefined {
@@ -277,11 +284,11 @@ function walk(ctx: Lint.WalkContext<IOptions>, checker: ts.TypeChecker) {
277284
case 1:
278285
return [signatures[0], true];
279286
default:
280-
const str = checker.signatureToString(signatures[0]);
287+
const str = checker.signatureToString(signatures[0], undefined, formatFlags);
281288
const withoutReturn = removeSignatureReturn(str);
282289
let returnUsable = true;
283290
for (let i = 1; i < signatures.length; ++i) { // check if all signatures are the same
284-
const sig = checker.signatureToString(signatures[i]);
291+
const sig = checker.signatureToString(signatures[i], undefined, formatFlags);
285292
if (str !== sig) {
286293
if (withoutReturn !== removeSignatureReturn(sig))
287294
return;
@@ -293,17 +300,11 @@ function walk(ctx: Lint.WalkContext<IOptions>, checker: ts.TypeChecker) {
293300
}
294301
}
295302

296-
function removeSignatureReturn(str: string) {
297-
let open = 1;
298-
let i = 1;
299-
for (; open !== 0; ++i) {
300-
if (str[i] === '(') {
301-
++open;
302-
} else if (str[i] === ')') {
303-
--open;
304-
}
305-
}
306-
return str.substr(0, i + 1);
303+
function removeSignatureReturn(str: string): string {
304+
const sourceFile = ts.createSourceFile('tmp.ts', `var a:${str}`, ts.ScriptTarget.ESNext);
305+
const signature = <ts.FunctionOrConstructorTypeNode>(<ts.VariableStatement>sourceFile.statements[0])
306+
.declarationList.declarations[0].type!;
307+
return sourceFile.text.substring(6, signature.parameters.end + 1);
307308
}
308309

309310
function getSignaturesOfType(type: ts.Type): ts.Signature[] {

test/rules/no-unnecessary-type-annotation/default/test.ts.lint

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ take<(((a: string) => void) | ((a: string) => void)) & {name?: string}>((a: stri
5353
// only return type differs - don't complain about the return annotation
5454
take<((a: string) => string) | ((a: string) => number)>((a: string): string => a);
5555
~~~~~~~~ [fail]
56+
take<(<T>(a: string) => string) | (<T>(a: string) => number)>((a: string): string => a);
57+
~~~~~~~~ [fail]
58+
take<(<T extends () => void>(a: string) => string) | (<T extends () => void>(a: string) => number)>((a: string): string => a);
59+
~~~~~~~~ [fail]
5660

5761
(function(foo: "foo") {})("foo");
5862
(function(foo: string) {})("foo");

0 commit comments

Comments
 (0)