-
-
Notifications
You must be signed in to change notification settings - Fork 63
/
identicality.ts
84 lines (70 loc) · 3.07 KB
/
identicality.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import {CallExpression, TypeChecker} from '@tsd/typescript';
import {Diagnostic} from '../../interfaces';
import {makeDiagnostic} from '../../utils';
/**
* Verifies that the argument of the assertion is identical to the generic type of the assertion.
*
* @param checker - The TypeScript type checker.
* @param nodes - The `expectType` AST nodes.
* @return List of custom diagnostics.
*/
export const isIdentical = (checker: TypeChecker, nodes: Set<CallExpression>): Diagnostic[] => {
const diagnostics: Diagnostic[] = [];
if (!nodes) {
return diagnostics;
}
for (const node of nodes) {
if (!node.typeArguments) {
// Skip if the node does not have generics
continue;
}
// Retrieve the type to be expected. This is the type inside the generic.
const expectedType = checker.getTypeFromTypeNode(node.typeArguments[0]);
// Retrieve the argument type. This is the type to be checked.
const argumentType = checker.getTypeAtLocation(node.arguments[0]);
if (!checker.isTypeAssignableTo(argumentType, expectedType)) {
// The argument type is not assignable to the expected type. TypeScript will catch this for us.
continue;
}
if (!checker.isTypeAssignableTo(expectedType, argumentType)) {
/**
* The expected type is not assignable to the argument type, but the argument type is
* assignable to the expected type. This means our type is too wide.
*/
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is declared too wide for argument type \`${checker.typeToString(argumentType)}\`.`));
} else if (!checker.isTypeIdenticalTo(expectedType, argumentType)) {
/**
* The expected type and argument type are assignable in both directions. We still have to check
* if the types are identical. See https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.11.2.
*/
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is not identical to argument type \`${checker.typeToString(argumentType)}\`.`));
}
}
return diagnostics;
};
/**
* Verifies that the argument of the assertion is not identical to the generic type of the assertion.
*
* @param checker - The TypeScript type checker.
* @param nodes - The `expectNotType` AST nodes.
* @return List of custom diagnostics.
*/
export const isNotIdentical = (checker: TypeChecker, nodes: Set<CallExpression>): Diagnostic[] => {
const diagnostics: Diagnostic[] = [];
if (!nodes) {
return diagnostics;
}
for (const node of nodes) {
if (!node.typeArguments) {
// Skip if the node does not have generics
continue;
}
// Retrieve the type to be expected. This is the type inside the generic.
const expectedType = checker.getTypeFromTypeNode(node.typeArguments[0]);
const argumentType = checker.getTypeAtLocation(node.arguments[0]);
if (checker.isTypeIdenticalTo(expectedType, argumentType)) {
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is identical to argument type \`${checker.typeToString(argumentType)}\`.`));
}
}
return diagnostics;
};