-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support ||s and type normalization for comparisons (#634)
## PR Checklist - [x] Addresses an existing open issue: fixes #108; fixes #109 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/eslint-plugin-expect-type/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/eslint-plugin-expect-type/blob/main/.github/CONTRIBUTING.md) were taken ## Overview #108: Adopts the strategy used in DefinitelyTyped-tools of printing types into a full in-memory TypeScript source file. Union types like `string | number` and `number | string` that mean the same thing but are in a different order will finally be considered compatible! 🥳 #109: Also adds "candidate" checking to support `||` types, while I'm in the area. `$ExpectType 0 || number` will now match `0` and `number` nicely. _Also_ adds more test coverage around ordering, parenthesis, and readonly arrays. 💖
- Loading branch information
1 parent
f750672
commit 8724da8
Showing
7 changed files
with
238 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Code based on DefinitelyTyped-tools implementation: | ||
// https://github.com/microsoft/DefinitelyTyped-tools/blob/42484ff245f6f18018de729f12c9a28436daa08a/packages/eslint-plugin/src/rules/expect.ts#L466 | ||
|
||
import ts from "typescript"; | ||
|
||
export function normalizedTypeToString(type: string) { | ||
const sourceFile = ts.createSourceFile( | ||
"foo.ts", | ||
`declare var x: ${type};`, | ||
ts.ScriptTarget.Latest, | ||
); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const typeNode = (sourceFile.statements[0] as ts.VariableStatement) | ||
.declarationList.declarations[0].type!; | ||
|
||
const printer = ts.createPrinter(); | ||
function print(node: ts.Node) { | ||
return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); | ||
} | ||
|
||
// TODO: pass undefined instead once all supported TS versions support it per: | ||
// https://github.com/microsoft/TypeScript/pull/52941 | ||
const context = ts.nullTransformationContext; | ||
|
||
function visit(node: ts.Node) { | ||
node = ts.visitEachChild(node, visit, context); | ||
|
||
if (ts.isUnionTypeNode(node)) { | ||
const types = node.types | ||
.map((t) => [t, print(t)] as const) | ||
.sort((a, b) => (a[1] < b[1] ? -1 : 1)) | ||
.map((t) => t[0]); | ||
return ts.factory.updateUnionTypeNode( | ||
node, | ||
ts.factory.createNodeArray(types), | ||
); | ||
} | ||
|
||
if ( | ||
ts.isTypeOperatorNode(node) && | ||
node.operator === ts.SyntaxKind.ReadonlyKeyword && | ||
ts.isArrayTypeNode(node.type) | ||
) { | ||
// It's possible that this would conflict with a library which defines their own type with this name, | ||
// but that's unlikely (and was not previously handled in a prior revision of type string normalization). | ||
return ts.factory.createTypeReferenceNode("ReadonlyArray", [ | ||
skipTypeParentheses(node.type.elementType), | ||
]); | ||
} | ||
|
||
return node; | ||
} | ||
|
||
const visited = visit(typeNode); | ||
return print(visited); | ||
} | ||
|
||
function skipTypeParentheses(node: ts.TypeNode): ts.TypeNode { | ||
while (ts.isParenthesizedTypeNode(node)) { | ||
node = node.type; | ||
} | ||
return node; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
{ | ||
"SnapshotMatches": "{ a: number; b: \"b\"; c: string; }", | ||
"TypeSnapshotDoNotMatch": "{ a: number; b: \"b\"; c: boolean; }" | ||
"SnapshotMatchesOr": "{ a: number; b: \"b\"; c: string; } || { d: boolean }", | ||
"TypeSnapshotDoNotMatch": "{ a: number; b: \"b\"; c: boolean; }", | ||
"TypeSnapshotDoNotMatchOr": "{ a: number; b: \"b\"; c: boolean; } { d: boolean }" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { TransformationContext } from "typescript"; | ||
|
||
declare module "typescript" { | ||
export const nullTransformationContext: TransformationContext; | ||
} |
Oops, something went wrong.