0
# Type Constraints and Context
1
2
Tools for working with TypeScript's type constraint system and contextual typing. These utilities are essential for understanding generic types, type inference, and the relationships between types in different contexts.
3
4
## Capabilities
5
6
### Type Constraint Resolution
7
8
Functions for resolving generic type constraints and understanding type parameter relationships.
9
10
```typescript { .api }
11
/**
12
* Resolves a node's type, returning the type's generic constraint if it has one.
13
* Warning: If the type is generic without a constraint, returns the type as-is
14
* rather than `unknown`. Check for ts.TypeFlags.TypeParameter to detect this case.
15
*/
16
function getConstrainedTypeAtLocation(
17
services: ParserServicesWithTypeInformation,
18
node: TSESTree.Node
19
): ts.Type;
20
```
21
22
**Usage Examples:**
23
24
```typescript
25
import { getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";
26
import * as ts from "typescript";
27
28
// In an ESLint rule
29
export default {
30
create(context) {
31
const services = context.parserServices;
32
33
return {
34
FunctionDeclaration(node) {
35
// Get the constrained type for a function parameter
36
if (node.params.length > 0) {
37
const param = node.params[0];
38
const constrainedType = getConstrainedTypeAtLocation(services, param);
39
40
// Check if it's a type parameter without constraint
41
if (constrainedType.flags & ts.TypeFlags.TypeParameter) {
42
// Handle unconstrained type parameter
43
context.report({
44
node: param,
45
messageId: "unconstrainedGeneric"
46
});
47
}
48
}
49
}
50
};
51
}
52
};
53
```
54
55
### Contextual Type Analysis
56
57
Functions for understanding the contextual type expectations in TypeScript expressions.
58
59
```typescript { .api }
60
/**
61
* Returns the contextual type of a given node - the type of the target
62
* the node is going into (e.g., function parameter type, variable declaration type)
63
*/
64
function getContextualType(checker: ts.TypeChecker, node: ts.Expression): ts.Type | undefined;
65
```
66
67
**Usage Examples:**
68
69
```typescript
70
import { getContextualType } from "@typescript-eslint/type-utils";
71
72
// In an ESLint rule checking function arguments
73
export default {
74
create(context) {
75
const services = context.parserServices;
76
const checker = services.program.getTypeChecker();
77
78
return {
79
CallExpression(node) {
80
// Check each argument against its expected contextual type
81
node.arguments.forEach((arg, index) => {
82
const tsArg = services.esTreeNodeToTSNodeMap.get(arg);
83
const contextualType = getContextualType(checker, tsArg);
84
85
if (contextualType) {
86
const actualType = checker.getTypeAtLocation(tsArg);
87
88
// Compare actual vs expected type
89
if (!checker.isTypeAssignableTo(actualType, contextualType)) {
90
context.report({
91
node: arg,
92
messageId: "typeArgumentMismatch",
93
data: {
94
expected: checker.typeToString(contextualType),
95
actual: checker.typeToString(actualType)
96
}
97
});
98
}
99
}
100
});
101
}
102
};
103
}
104
};
105
```
106
107
## Advanced Constraint Analysis
108
109
### Generic Type Constraint Checking
110
111
```typescript
112
// Example: Analyzing generic constraints in function declarations
113
import { getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";
114
import * as ts from "typescript";
115
116
function analyzeGenericConstraints(
117
services: ParserServicesWithTypeInformation,
118
functionNode: TSESTree.FunctionDeclaration
119
) {
120
if (functionNode.typeParameters) {
121
functionNode.typeParameters.params.forEach(typeParam => {
122
const constrainedType = getConstrainedTypeAtLocation(services, typeParam);
123
124
if (constrainedType.flags & ts.TypeFlags.TypeParameter) {
125
// Unconstrained type parameter
126
console.log(`Unconstrained type parameter: ${typeParam.name.name}`);
127
} else {
128
// Has constraint
129
const checker = services.program.getTypeChecker();
130
const constraintName = checker.typeToString(constrainedType);
131
console.log(`Type parameter ${typeParam.name.name} constrained to: ${constraintName}`);
132
}
133
});
134
}
135
}
136
```
137
138
### Contextual Type Validation
139
140
```typescript
141
// Example: Validating array literal elements against contextual type
142
import { getContextualType } from "@typescript-eslint/type-utils";
143
144
function validateArrayLiteralTypes(
145
services: ParserServicesWithTypeInformation,
146
arrayNode: TSESTree.ArrayExpression
147
) {
148
const checker = services.program.getTypeChecker();
149
const tsArrayNode = services.esTreeNodeToTSNodeMap.get(arrayNode);
150
const contextualType = getContextualType(checker, tsArrayNode);
151
152
if (contextualType && checker.isArrayType(contextualType)) {
153
const elementType = checker.getTypeArguments(contextualType as ts.TypeReference)[0];
154
155
arrayNode.elements.forEach(element => {
156
if (element) {
157
const tsElement = services.esTreeNodeToTSNodeMap.get(element);
158
const elementActualType = checker.getTypeAtLocation(tsElement);
159
160
if (!checker.isTypeAssignableTo(elementActualType, elementType)) {
161
console.log("Array element type mismatch");
162
}
163
}
164
});
165
}
166
}
167
```
168
169
### Inference Context Analysis
170
171
```typescript
172
// Example: Understanding type inference in variable declarations
173
import { getContextualType, getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";
174
175
function analyzeTypeInference(
176
services: ParserServicesWithTypeInformation,
177
variableDeclarator: TSESTree.VariableDeclarator
178
) {
179
const checker = services.program.getTypeChecker();
180
181
// Get the declared type constraint
182
if (variableDeclarator.id.typeAnnotation) {
183
const declaredType = getConstrainedTypeAtLocation(services, variableDeclarator.id);
184
console.log(`Declared type: ${checker.typeToString(declaredType)}`);
185
}
186
187
// Get the contextual type from initializer
188
if (variableDeclarator.init) {
189
const tsInit = services.esTreeNodeToTSNodeMap.get(variableDeclarator.init);
190
const contextualType = getContextualType(checker, tsInit);
191
192
if (contextualType) {
193
console.log(`Contextual type: ${checker.typeToString(contextualType)}`);
194
}
195
196
// Compare inferred vs contextual
197
const inferredType = checker.getTypeAtLocation(tsInit);
198
console.log(`Inferred type: ${checker.typeToString(inferredType)}`);
199
}
200
}
201
```
202
203
## Common Patterns
204
205
### Generic Function Analysis
206
207
```typescript
208
// Analyze generic function constraints and usage
209
function analyzeGenericFunction(node: TSESTree.FunctionDeclaration, services: ParserServicesWithTypeInformation) {
210
const checker = services.program.getTypeChecker();
211
212
// Check type parameters
213
if (node.typeParameters) {
214
node.typeParameters.params.forEach(param => {
215
const constrainedType = getConstrainedTypeAtLocation(services, param);
216
// Analyze constraint
217
});
218
}
219
220
// Check parameter contextual types
221
node.params.forEach(param => {
222
if (param.type === "Identifier" && param.init) {
223
const tsInit = services.esTreeNodeToTSNodeMap.get(param.init);
224
const contextualType = getContextualType(checker, tsInit);
225
// Validate default parameter against contextual type
226
}
227
});
228
}
229
```