From 539efb13833270d589010257a304283831e97ee1 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 22 Aug 2017 08:12:02 -0700 Subject: [PATCH 1/3] For $ExpectType assertions, alphabetically sort unions and intersections --- src/rules/expectRule.ts | 15 ++++++++++++++- test/expect/expectType.ts.lint | 12 ++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/rules/expectRule.ts b/src/rules/expectRule.ts index 99cc14de..aa72c6ef 100644 --- a/src/rules/expectRule.ts +++ b/src/rules/expectRule.ts @@ -296,7 +296,7 @@ function getExpectTypeFailures( const type = checker.getTypeAtLocation(node); - const actual = checker.typeToString(type, /*enclosingDeclaration*/ undefined, ts.TypeFormatFlags.NoTruncation); + const actual = fixupUnions(checker.typeToString(type, /*enclosingDeclaration*/ undefined, ts.TypeFormatFlags.NoTruncation)); if (actual !== expected) { unmetExpectations.push({ node, expected, actual }); } @@ -308,6 +308,19 @@ function getExpectTypeFailures( } } +function fixupUnions(s: string): string { + const splitter = " | "; + return s.split(splitter).map(fixupIntersections).sort().join(splitter); +} + +function fixupIntersections(s: string): string { + if (s.startsWith("(") && s.endsWith(")")) { + return `(${fixupIntersections(s.slice(1, s.length - 1))})`; + } + const splitter = " & "; + return s.split(splitter).sort().join(splitter); +} + function lineOfPosition(pos: number, sourceFile: SourceFile): number { return sourceFile.getLineAndCharacterOfPosition(pos).line; } diff --git a/test/expect/expectType.ts.lint b/test/expect/expectType.ts.lint index 2b830753..e964dd1f 100644 --- a/test/expect/expectType.ts.lint +++ b/test/expect/expectType.ts.lint @@ -18,3 +18,15 @@ declare function f( // Test that we never truncate types. f; // $ExpectType (one: number, two: [number, number], three: [number, number, number], four: [number, number, number, number]) => number + +// Test that we order unions alphabetically. +declare const x: "foo" | "bar"; +x; // $ExpectType "bar" | "foo" + +type A = { a: number }; +type B = { b: number }; +type C = { c: number }; + +// Test that we order intersections too. +declare const cba: C | B & A; +cba; // $ExpectType (A & B) | C From 7f98937273e435f15e38fbf24335563594016058 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 22 Aug 2017 11:30:31 -0700 Subject: [PATCH 2/3] Avoid delving inside object types --- src/rules/expectRule.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rules/expectRule.ts b/src/rules/expectRule.ts index aa72c6ef..1d9f85f8 100644 --- a/src/rules/expectRule.ts +++ b/src/rules/expectRule.ts @@ -296,7 +296,8 @@ function getExpectTypeFailures( const type = checker.getTypeAtLocation(node); - const actual = fixupUnions(checker.typeToString(type, /*enclosingDeclaration*/ undefined, ts.TypeFormatFlags.NoTruncation)); + const actual = fixupType( + checker.typeToString(type, /*enclosingDeclaration*/ undefined, ts.TypeFormatFlags.NoTruncation)); if (actual !== expected) { unmetExpectations.push({ node, expected, actual }); } @@ -308,6 +309,14 @@ function getExpectTypeFailures( } } +function fixupType(s: string): string { + // Don't mess up `{ a: number | string }` into `string } | { a: number` + if (s.includes("{")) { + return s; + } + return fixupUnions(s); +} + function fixupUnions(s: string): string { const splitter = " | "; return s.split(splitter).map(fixupIntersections).sort().join(splitter); From 1ed26fb7d1105be97209db5364aac059a953f05b Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 28 Aug 2017 08:11:31 -0700 Subject: [PATCH 3/3] Respond to PR comments --- src/rules/expectRule.ts | 12 ++++++++---- .../wrong/types/bad-url-username/index.d.ts.lint | 2 +- test/dt-header/wrong/types/bad-url/index.d.ts.lint | 2 +- test/expect/expectType.ts.lint | 10 ++++++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/rules/expectRule.ts b/src/rules/expectRule.ts index 1d9f85f8..d1478de0 100644 --- a/src/rules/expectRule.ts +++ b/src/rules/expectRule.ts @@ -296,10 +296,14 @@ function getExpectTypeFailures( const type = checker.getTypeAtLocation(node); - const actual = fixupType( - checker.typeToString(type, /*enclosingDeclaration*/ undefined, ts.TypeFormatFlags.NoTruncation)); + const actual = checker.typeToString(type, /*enclosingDeclaration*/ undefined, ts.TypeFormatFlags.NoTruncation); if (actual !== expected) { - unmetExpectations.push({ node, expected, actual }); + console.log("!!!", actual); + const actualSorted = fixupType(actual); + if (actualSorted !== expected) { + console.log("???", actualSorted); + unmetExpectations.push({ node, expected, actual }); + } } typeAssertions.delete(line); @@ -311,7 +315,7 @@ function getExpectTypeFailures( function fixupType(s: string): string { // Don't mess up `{ a: number | string }` into `string } | { a: number` - if (s.includes("{")) { + if (/[\<\(\[\{]/.test(s)) { return s; } return fixupUnions(s); diff --git a/test/dt-header/wrong/types/bad-url-username/index.d.ts.lint b/test/dt-header/wrong/types/bad-url-username/index.d.ts.lint index 91b3d985..0ac32c82 100644 --- a/test/dt-header/wrong/types/bad-url-username/index.d.ts.lint +++ b/test/dt-header/wrong/types/bad-url-username/index.d.ts.lint @@ -1,5 +1,5 @@ // Type definitions for dt-header 1.0 // Project: https://github.com/bobby-headers/dt-header // Definitions by: Jane Doe - ~ [Error parsing header. Expected: '>'] + ~ [Error parsing header. Expected: /\/] // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped diff --git a/test/dt-header/wrong/types/bad-url/index.d.ts.lint b/test/dt-header/wrong/types/bad-url/index.d.ts.lint index c71501e1..35f39b1a 100644 --- a/test/dt-header/wrong/types/bad-url/index.d.ts.lint +++ b/test/dt-header/wrong/types/bad-url/index.d.ts.lint @@ -1,5 +1,5 @@ // Type definitions for dt-header 1.0 // Project: https://github.com/bobby-headers/dt-header // Definitions by: Jane Doe - ~ [Error parsing header. Expected: '/] // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped diff --git a/test/expect/expectType.ts.lint b/test/expect/expectType.ts.lint index e964dd1f..24c72118 100644 --- a/test/expect/expectType.ts.lint +++ b/test/expect/expectType.ts.lint @@ -19,14 +19,16 @@ declare function f( // Test that we never truncate types. f; // $ExpectType (one: number, two: [number, number], three: [number, number, number], four: [number, number, number, number]) => number -// Test that we order unions alphabetically. +// Test that a union could be either exactly what tsc output, or a sorted version of that. declare const x: "foo" | "bar"; +x; // $ExpectType "foo" | "bar" x; // $ExpectType "bar" | "foo" type A = { a: number }; type B = { b: number }; type C = { c: number }; -// Test that we order intersections too. -declare const cba: C | B & A; -cba; // $ExpectType (A & B) | C +// Test that we don't try to sort nested unions. +declare class Generic {} +declare const generic: Generic; +generic; // $ExpectType Generic