Skip to content

Commit 5cf94bc

Browse files
committed
Add more tests for literal types and objects, fix issues
1 parent 4b2fb1e commit 5cf94bc

File tree

4 files changed

+171
-29
lines changed

4 files changed

+171
-29
lines changed

src/rules/enforce-relation-types.test.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,28 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
8686
},
8787
],
8888
},
89+
{
90+
code: `class Entity {
91+
@OneToOne(() => Other, {})
92+
@JoinColumn()
93+
other: Other;
94+
}`,
95+
errors: [
96+
{
97+
messageId: 'typescript_typeorm_relation_nullable_by_default',
98+
suggestions: [
99+
{
100+
messageId: 'typescript_typeorm_relation_suggestion',
101+
output: `class Entity {
102+
@OneToOne(() => Other, {})
103+
@JoinColumn()
104+
other: Other | null;
105+
}`,
106+
},
107+
],
108+
},
109+
],
110+
},
89111
{
90112
code: `class Entity {
91113
@OneToOne(() => Other)
@@ -108,6 +130,50 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
108130
},
109131
],
110132
},
133+
{
134+
code: `class Entity {
135+
@OneToOne(() => Other)
136+
@JoinColumn()
137+
other: string;
138+
}`,
139+
errors: [
140+
{
141+
messageId: 'typescript_typeorm_relation_mismatch',
142+
suggestions: [
143+
{
144+
messageId: 'typescript_typeorm_relation_suggestion',
145+
output: `class Entity {
146+
@OneToOne(() => Other)
147+
@JoinColumn()
148+
other: Other | null;
149+
}`,
150+
},
151+
],
152+
},
153+
],
154+
},
155+
{
156+
code: `class Entity {
157+
@OneToOne(() => Other)
158+
@JoinColumn()
159+
other: "other";
160+
}`,
161+
errors: [
162+
{
163+
messageId: 'typescript_typeorm_relation_mismatch',
164+
suggestions: [
165+
{
166+
messageId: 'typescript_typeorm_relation_suggestion',
167+
output: `class Entity {
168+
@OneToOne(() => Other)
169+
@JoinColumn()
170+
other: Other | null;
171+
}`,
172+
},
173+
],
174+
},
175+
],
176+
},
111177
{
112178
code: `class Entity {
113179
@OneToOne(() => Other, { nullable: false })
@@ -170,6 +236,26 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
170236
},
171237
],
172238
},
239+
{
240+
code: `class Entity {
241+
@OneToMany(() => Other, (other) => other.entity)
242+
others: string[];
243+
}`,
244+
errors: [
245+
{
246+
messageId: 'typescript_typeorm_relation_mismatch',
247+
suggestions: [
248+
{
249+
messageId: 'typescript_typeorm_relation_suggestion',
250+
output: `class Entity {
251+
@OneToMany(() => Other, (other) => other.entity)
252+
others: Other[];
253+
}`,
254+
},
255+
],
256+
},
257+
],
258+
},
173259
{
174260
code: `class Entity {
175261
@ManyToOne(() => Other)
@@ -197,6 +283,26 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
197283
},
198284
],
199285
},
286+
{
287+
code: `class Entity {
288+
@ManyToOne(() => Other, {})
289+
other: Other;
290+
}`,
291+
errors: [
292+
{
293+
messageId: 'typescript_typeorm_relation_nullable_by_default',
294+
suggestions: [
295+
{
296+
messageId: 'typescript_typeorm_relation_suggestion',
297+
output: `class Entity {
298+
@ManyToOne(() => Other, {})
299+
other: Other | null;
300+
}`,
301+
},
302+
],
303+
},
304+
],
305+
},
200306
{
201307
code: `class Entity {
202308
@ManyToOne(() => Other)
@@ -217,6 +323,26 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
217323
},
218324
],
219325
},
326+
{
327+
code: `class Entity {
328+
@ManyToOne(() => Other)
329+
other: string;
330+
}`,
331+
errors: [
332+
{
333+
messageId: 'typescript_typeorm_relation_mismatch',
334+
suggestions: [
335+
{
336+
messageId: 'typescript_typeorm_relation_suggestion',
337+
output: `class Entity {
338+
@ManyToOne(() => Other)
339+
other: Other | null;
340+
}`,
341+
},
342+
],
343+
},
344+
],
345+
},
220346
{
221347
code: `class Entity {
222348
@ManyToOne(() => Other, { nullable: false })
@@ -281,5 +407,27 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
281407
},
282408
],
283409
},
410+
{
411+
code: `class Entity {
412+
@ManyToMany(() => Other)
413+
@JoinTable()
414+
others: string;
415+
}`,
416+
errors: [
417+
{
418+
messageId: 'typescript_typeorm_relation_mismatch',
419+
suggestions: [
420+
{
421+
messageId: 'typescript_typeorm_relation_suggestion',
422+
output: `class Entity {
423+
@ManyToMany(() => Other)
424+
@JoinTable()
425+
others: Other[];
426+
}`,
427+
},
428+
],
429+
},
430+
],
431+
},
284432
],
285433
});

src/rules/enforce-relation-types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const enforceColumnTypes = createRule({
4848
typescript_typeorm_relation_suggestion:
4949
'Change the type of {{ propertyName }} to {{ expectedValue }}.',
5050
typescript_typeorm_relation_nullable_by_default_suggestion:
51-
'Make the {{ relation }} relation nullable.',
51+
'Make the {{ relation }} relation nullable: false.',
5252
},
5353
schema: [],
5454
},

src/utils/relationType.ts

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,18 @@ interface RelationType {
1010
export const relationTypes = ['OneToOne', 'OneToMany', 'ManyToOne', 'ManyToMany'] as const;
1111
export type Relation = (typeof relationTypes)[number];
1212

13-
export function convertTypeToRelationType(arg: TSESTree.TypeNode): RelationType | undefined {
13+
export function convertTypeToRelationType(arg: TSESTree.TypeNode): RelationType {
1414
switch (arg.type) {
1515
case AST_NODE_TYPES.TSTypeReference: {
16-
if (arg.typeName.type === AST_NODE_TYPES.Identifier) {
17-
return { name: arg.typeName.name, isArray: false, nullable: false };
18-
}
19-
return undefined;
16+
return {
17+
name: arg.typeName.type === AST_NODE_TYPES.Identifier ? arg.typeName.name : '',
18+
isArray: false,
19+
nullable: false,
20+
};
2021
}
2122
case AST_NODE_TYPES.TSArrayType: {
22-
if (
23-
arg.elementType.type === AST_NODE_TYPES.TSTypeReference &&
24-
arg.elementType.typeName.type === AST_NODE_TYPES.Identifier
25-
) {
26-
return { name: arg.elementType.typeName.name, isArray: true, nullable: false };
27-
}
28-
return undefined;
23+
const item = convertTypeToRelationType(arg.elementType);
24+
return { ...item, isArray: true };
2925
}
3026
case AST_NODE_TYPES.TSNullKeyword: {
3127
return { name: '', isArray: false, nullable: true };
@@ -34,14 +30,11 @@ export function convertTypeToRelationType(arg: TSESTree.TypeNode): RelationType
3430
return arg.types.reduce(
3531
(acc, currentNode) => {
3632
const current = convertTypeToRelationType(currentNode);
37-
if (current) {
38-
return {
39-
name: acc.name || current.name,
40-
isArray: acc.isArray || current.isArray,
41-
nullable: acc.nullable || current.nullable,
42-
};
43-
}
44-
return acc;
33+
return {
34+
name: acc.name || current.name,
35+
isArray: acc.isArray || current.isArray,
36+
nullable: acc.nullable || current.nullable,
37+
};
4538
},
4639
{
4740
name: '',
@@ -50,8 +43,9 @@ export function convertTypeToRelationType(arg: TSESTree.TypeNode): RelationType
5043
} as RelationType
5144
);
5245
}
53-
default:
54-
return undefined;
46+
default: {
47+
return { name: '', isArray: false, nullable: false };
48+
}
5549
}
5650
}
5751

@@ -74,11 +68,9 @@ export function convertArgumentToRelationType(
7468
}
7569
// OneToOne, ManyToOne
7670
const options = findObjectArgument(restArguments);
77-
const { nullable } = options
78-
? (parseObjectLiteral(options) as { nullable: boolean })
79-
: { nullable: true };
71+
const parsedOptions = parseObjectLiteral(options) as { nullable?: boolean } | undefined;
8072

81-
return { name: otherEntity, isArray: false, nullable };
73+
return { name: otherEntity, isArray: false, nullable: parsedOptions?.nullable ?? true };
8274
}
8375

8476
export function isTypesEqual(

src/utils/treeTraversal.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ export function findObjectArgument(
4545
return args.find((arg) => arg.type === AST_NODE_TYPES.ObjectExpression);
4646
}
4747

48-
export function parseObjectLiteral(objectLiteral: TSESTree.Node): Record<string, unknown> {
49-
if (objectLiteral.type === AST_NODE_TYPES.ObjectExpression) {
48+
export function parseObjectLiteral(
49+
objectLiteral: TSESTree.Node | undefined
50+
): Record<string, unknown> {
51+
if (objectLiteral?.type === AST_NODE_TYPES.ObjectExpression) {
5052
return objectLiteral.properties.reduce((parsedObject, prop) => {
5153
if (
5254
prop.type === AST_NODE_TYPES.Property &&

0 commit comments

Comments
 (0)