Skip to content

Commit 8c38994

Browse files
authored
no-duplicate-spread-property: handle intersection types (#449)
1 parent 645a7cb commit 8c38994

File tree

4 files changed

+110
-3
lines changed

4 files changed

+110
-3
lines changed

baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint

+35
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,38 @@ var v: any;
260260
get foo() { return 2; },
261261
set foo(v: number) {},
262262
});
263+
264+
({
265+
foo: 1,
266+
~~~ [error no-duplicate-spread-property: Property 'foo' is overridden later.]
267+
bar: 1,
268+
~~~ [error no-duplicate-spread-property: Property 'bar' is overridden later.]
269+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
270+
bas: 1,
271+
});
272+
273+
({
274+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
275+
foo: 1,
276+
bar: 1,
277+
bas: 1,
278+
});
279+
280+
({
281+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
282+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [error no-duplicate-spread-property: All properties of this object are overridden later.]
283+
foo: 1,
284+
bar: 1,
285+
baz: 1,
286+
});
287+
288+
({
289+
bar: 1,
290+
~~~ [error no-duplicate-spread-property: Property 'bar' is overridden later.]
291+
...get<{bar: number} & Record<string, number>>(),
292+
});
293+
294+
({
295+
...get<{bar: number} & Record<string, number>>(),
296+
bar: 1,
297+
});

baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint

+31
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,34 @@ var v: any;
232232
get foo() { return 2; },
233233
set foo(v: number) {},
234234
});
235+
236+
({
237+
foo: 1,
238+
bar: 1,
239+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
240+
bas: 1,
241+
});
242+
243+
({
244+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
245+
foo: 1,
246+
bar: 1,
247+
bas: 1,
248+
});
249+
250+
({
251+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
252+
foo: 1,
253+
bar: 1,
254+
baz: 1,
255+
});
256+
257+
({
258+
bar: 1,
259+
...get<{bar: number} & Record<string, number>>(),
260+
});
261+
262+
({
263+
...get<{bar: number} & Record<string, number>>(),
264+
bar: 1,
265+
});

packages/mimir/src/rules/no-duplicate-spread-property.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TypedRule, excludeDeclarationFiles, requiresCompilerOption } from '@fimbul/ymir';
22
import * as ts from 'typescript';
3-
import { isReassignmentTarget, isObjectType, unionTypeParts, isClassLikeDeclaration, getPropertyName } from 'tsutils';
3+
import { isReassignmentTarget, isObjectType, unionTypeParts, isClassLikeDeclaration, getPropertyName, isIntersectionType } from 'tsutils';
44
import { lateBoundPropertyNames } from '../utils';
55

66
interface PropertyInfo {
@@ -88,11 +88,13 @@ export class Rule extends TypedRule {
8888

8989
private getPropertyInfoFromSpread(node: ts.Expression): PropertyInfo {
9090
const type = this.checker.getTypeAtLocation(node)!;
91-
return unionTypeParts(type).map(getPropertyInfoFromType).reduce(combinePropertyInfo);
91+
return unionTypeParts(type).map(getPropertyInfoFromType).reduce(unionPropertyInfo);
9292
}
9393
}
9494

9595
function getPropertyInfoFromType(type: ts.Type): PropertyInfo {
96+
if (isIntersectionType(type))
97+
return type.types.map(getPropertyInfoFromType).reduce(intersectPropertyInfo);
9698
if (!isObjectType(type))
9799
return emptyPropertyInfo;
98100
const result: PropertyInfo = {
@@ -118,10 +120,18 @@ function isSpreadableProperty(prop: ts.Symbol): boolean | undefined {
118120
return true;
119121
}
120122

121-
function combinePropertyInfo(a: PropertyInfo, b: PropertyInfo): PropertyInfo {
123+
function unionPropertyInfo(a: PropertyInfo, b: PropertyInfo): PropertyInfo {
122124
return {
123125
known: a.known && b.known,
124126
names: [...a.names, ...b.names],
125127
assignedNames: a.assignedNames.filter((name) => b.assignedNames.includes(name)),
126128
};
127129
}
130+
131+
function intersectPropertyInfo(a: PropertyInfo, b: PropertyInfo): PropertyInfo {
132+
return {
133+
known: a.known && b.known,
134+
names: [...a.names, ...b.names],
135+
assignedNames: [...a.assignedNames, ...b.assignedNames],
136+
};
137+
}

packages/mimir/test/no-duplicate-spread-property/test.ts

+31
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,34 @@ var v: any;
232232
get foo() { return 2; },
233233
set foo(v: number) {},
234234
});
235+
236+
({
237+
foo: 1,
238+
bar: 1,
239+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
240+
bas: 1,
241+
});
242+
243+
({
244+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
245+
foo: 1,
246+
bar: 1,
247+
bas: 1,
248+
});
249+
250+
({
251+
...get<{bar: number} & Record<'foo' | 'baz', number>>(),
252+
foo: 1,
253+
bar: 1,
254+
baz: 1,
255+
});
256+
257+
({
258+
bar: 1,
259+
...get<{bar: number} & Record<string, number>>(),
260+
});
261+
262+
({
263+
...get<{bar: number} & Record<string, number>>(),
264+
bar: 1,
265+
});

0 commit comments

Comments
 (0)