Skip to content

Commit 7fbefcd

Browse files
committed
feature: @putout/printer: type-checker: types
1 parent a4af38f commit 7fbefcd

8 files changed

Lines changed: 147 additions & 17 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
type TypeChecker = (path: unknown, options?: Record<string, unknown>) => boolean;
2+
3+
type CoverageEntry = {
4+
covered: Set<number>;
5+
typeNames: unknown[];
6+
};
7+
8+
type InstrumentOverride = {
9+
env?: {TYPE_CHECK?: string; [key: string]: unknown};
10+
coverage?: Map<string, CoverageEntry>;
11+
instrumentName?: string;
12+
[key: string]: unknown;
13+
};
14+
15+
export function instrument(
16+
typeNames: unknown[],
17+
fn: TypeChecker,
18+
overrides?: InstrumentOverride,
19+
): TypeChecker;
20+
21+
export function getCoverage(): Map<string, CoverageEntry>;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type CoverageEntry = {
2+
covered: Set<number>;
3+
typeNames: unknown[];
4+
};
5+
6+
export function report(coverage: Map<string, CoverageEntry>): [number, string];
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
type TypeChecker = (path: unknown, options?: Record<string, unknown>) => boolean;
2+
3+
type TypeCheckerOverride = {
4+
instrumentCoverage?: (
5+
typeNames: unknown[],
6+
fn: TypeChecker,
7+
overrides?: Record<string, unknown>,
8+
) => TypeChecker;
9+
instrumentName?: string;
10+
[key: string]: unknown;
11+
};
12+
13+
export function createTypeChecker(
14+
typeNames: unknown[],
15+
overrides?: TypeCheckerOverride,
16+
): TypeChecker;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
type WhenTestsEndsOverride = {
2+
log?: (message: string) => void;
3+
getCoverage?: () => Map<string, {covered: Set<number>; typeNames: unknown[]}>;
4+
report?: (coverage: Map<string, {covered: Set<number>; typeNames: unknown[]}>) => [number, string];
5+
[key: string]: unknown;
6+
};
7+
8+
export function whenTestsEnds(overrides?: WhenTestsEndsOverride): number;

test/printer.errors.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,25 @@ import {
55
visitors,
66
Format,
77
Semantics,
8-
Options,
9-
Visitors,
108
Visitor,
119
Printer,
1210
} from '#printer';
1311

1412
const {identifier} = types;
13+
const id = (a: unknown) => a;
1514

1615
// THROWS Expected 4 arguments, but got 1.
1716
maybeVisitor(1);
1817

1918
// THROWS Argument of type 'number' is not assignable to parameter of type 'Visitor'
2019
maybeVisitor(1, 2, 3, 4);
20+
2121
// THROWS Argument of type 'number' is not assignable to parameter of type 'Node'
2222
const a = maybeVisitor(visitors.ArrayExpression, 2, 3, 4);
2323

2424
// THROWS Type 'void' is not assignable to type 'string'
2525
const b: string = a;
26+
id(b);
2627

2728
// THROWS Argument of type 'number' is not assignable to parameter of type 'Node'
2829
print(1);
@@ -37,16 +38,17 @@ print(identifier('hello'), {
3738

3839
// THROWS Type '{}' is missing the following properties from type 'Format': indent, newline, space, splitter, quote
3940
const format: Format = {};
41+
id(format);
4042

4143
// THROWS Type '{}' is missing the following properties from type 'Semantics': roundBraces, comments, maxPropertiesInOneLine, maxSpecifiersInOneLine, and 3 more.
4244
const semantics: Semantics = {};
43-
44-
const opts: Options = {};
45+
id(semantics);
4546

4647
// THROWS Type '{}' is not assignable to type 'Visitor'
4748
const visitor: Visitor = {};
49+
id(visitor);
4850

49-
// THROWS Type '{}' is missing the following properties from type 'Printer': print, maybe, indent, traverse
50-
const printer: Printer = {};
51-
52-
const vt: Visitors = {};
51+
const fn = (printer: Printer) => printer;
52+
const printer = {};
53+
// THROWS Argument of type '{}' is not assignable to parameter of type 'Printer'.
54+
fn(printer);

test/type-checker.errors.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {createTypeChecker} from '#type-checker';
2+
3+
// THROWS Expected 1-2 arguments, but got 3.
4+
createTypeChecker([], {}, {});
5+
6+
// THROWS Expected 1-2 arguments, but got 0.
7+
createTypeChecker();
8+
9+
// THROWS Argument of type 'number' is not assignable to parameter of type 'unknown[]'.
10+
createTypeChecker(1);
11+
12+
const isBlockLike = createTypeChecker(['Program', 'BlockStatement']);
13+
14+
// THROWS Argument of type 'boolean' is not assignable to parameter of type 'Record<string, unknown>'
15+
isBlockLike({type: 'Program'}, true);
16+
17+
// THROWS Argument of type 'number' is not assignable to parameter of type 'Record<string, unknown>'
18+
isBlockLike({type: 'Program'}, 1);
19+

test/type-checker.types.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {createTypeChecker} from '#type-checker';
2+
import {instrument, getCoverage} from '#type-checker/instrument';
3+
import {report} from '#type-checker/report';
4+
import {whenTestsEnds} from '#type-checker/when-tests-ends';
5+
6+
// Simple string type names
7+
createTypeChecker(['Program', 'BlockStatement'])({type: 'Program'});
8+
9+
// Predicate function
10+
const isEmptyBody = (_a: {node: {body: {body: unknown[]}}}) => !_a.node.body.body.length;
11+
createTypeChecker([isEmptyBody])({node: {body: {body: []}}});
12+
13+
// Tuple with string
14+
createTypeChecker([
15+
['-', 'BlockStatement'],
16+
])({type: 'Program'});
17+
18+
// DSL string
19+
createTypeChecker([
20+
'+: parentPath -> ExportDefaultDeclaration',
21+
])({
22+
parentPath: {type: 'ExportDefaultDeclaration'},
23+
});
24+
25+
// DSL string with negation
26+
createTypeChecker([
27+
'+: parentPath -> !CallExpression',
28+
])({});
29+
30+
// Tuple with DSL and function
31+
const hasFnBody = (_a: {node: {body: {body: unknown[]}}}) => _a.node.body.body.length;
32+
createTypeChecker([
33+
['-: parentPath -> !', 'ExportDefaultDeclaration'],
34+
['+', hasFnBody],
35+
])({
36+
parentPath: {type: 'ExportDefaultDeclaration'},
37+
type: 'BlockStatement',
38+
node: {body: {body: []}},
39+
});
40+
41+
// Override with instrumentCoverage and instrumentName
42+
createTypeChecker([
43+
'+: -> BlockStatement',
44+
], {
45+
instrumentName: 'hello',
46+
});
47+
48+
// Call with options
49+
const hasFnBodyOptions = (_a: unknown, b: Record<string, unknown>) => b.is;
50+
createTypeChecker([
51+
['+', hasFnBodyOptions],
52+
])({
53+
type: 'FunctionDeclaration',
54+
}, {
55+
is: true,
56+
});
57+
58+
// instrument function
59+
instrument(['+: -> StringLiteral'], (_a: unknown) => true, {
60+
instrumentName: 'TYPE_CHECK',
61+
})({});
62+
63+
// getCoverage + report
64+
report(getCoverage());
65+
66+
// whenTestsEnds
67+
whenTestsEnds();

tsconfig.json

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)