From fcc672cf0db35f20016205bcfba22750c63323a1 Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Tue, 30 Oct 2018 20:33:20 +0100 Subject: [PATCH 1/2] no-duplicate-spread-property: handle intersection types Fixes: #448 --- .../default/test.ts.lint | 35 +++++++++++++++++++ .../loose/test.ts.lint | 31 ++++++++++++++++ .../src/rules/no-duplicate-spread-property.ts | 16 +++++++-- .../test/no-duplicate-spread-property/test.ts | 31 ++++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint b/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint index a385521e2..cab989682 100644 --- a/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint +++ b/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint @@ -260,3 +260,38 @@ var v: any; get foo() { return 2; }, set foo(v: number) {}, }); + +({ + foo: 1, + ~~~ [error no-duplicate-spread-property: Property 'foo' is overridden later.] + bar: 1, + ~~~ [error no-duplicate-spread-property: Property 'bar' is overridden later.] + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + bas: 1, +}); + +({ + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + foo: 1, + bar: 1, + bas: 1, +}); + +({ + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [error no-duplicate-spread-property: All properties of this object are overridden later.] + foo: 1, + bar: 1, + baz: 1, +}); + +({ + bar: 1, + ~~~ [error no-duplicate-spread-property: Property 'bar' is overridden later.] + ...get<{bar: number} & Record>(), +}); + +({ + ...get<{bar: number} & Record>(), + bar: 1, +}); diff --git a/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint b/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint index 488d1716f..b87bf0063 100644 --- a/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint +++ b/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint @@ -232,3 +232,34 @@ var v: any; get foo() { return 2; }, set foo(v: number) {}, }); + +({ + foo: 1, + bar: 1, + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + bas: 1, +}); + +({ + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + foo: 1, + bar: 1, + bas: 1, +}); + +({ + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + foo: 1, + bar: 1, + baz: 1, +}); + +({ + bar: 1, + ...get<{bar: number} & Record>(), +}); + +({ + ...get<{bar: number} & Record>(), + bar: 1, +}); diff --git a/packages/mimir/src/rules/no-duplicate-spread-property.ts b/packages/mimir/src/rules/no-duplicate-spread-property.ts index 030ca6bdb..bcce3d98b 100644 --- a/packages/mimir/src/rules/no-duplicate-spread-property.ts +++ b/packages/mimir/src/rules/no-duplicate-spread-property.ts @@ -1,6 +1,6 @@ import { TypedRule, excludeDeclarationFiles, requiresCompilerOption } from '@fimbul/ymir'; import * as ts from 'typescript'; -import { isReassignmentTarget, isObjectType, unionTypeParts, isClassLikeDeclaration, getPropertyName } from 'tsutils'; +import { isReassignmentTarget, isObjectType, unionTypeParts, isClassLikeDeclaration, getPropertyName, isIntersectionType } from 'tsutils'; import { lateBoundPropertyNames } from '../utils'; interface PropertyInfo { @@ -88,11 +88,13 @@ export class Rule extends TypedRule { private getPropertyInfoFromSpread(node: ts.Expression): PropertyInfo { const type = this.checker.getTypeAtLocation(node)!; - return unionTypeParts(type).map(getPropertyInfoFromType).reduce(combinePropertyInfo); + return unionTypeParts(type).map(getPropertyInfoFromType).reduce(unionPropertyInfo); } } function getPropertyInfoFromType(type: ts.Type): PropertyInfo { + if (isIntersectionType(type)) + return type.types.map(getPropertyInfoFromType).reduce(intersectPropertyInfo); if (!isObjectType(type)) return emptyPropertyInfo; const result: PropertyInfo = { @@ -118,10 +120,18 @@ function isSpreadableProperty(prop: ts.Symbol): boolean | undefined { return true; } -function combinePropertyInfo(a: PropertyInfo, b: PropertyInfo): PropertyInfo { +function unionPropertyInfo(a: PropertyInfo, b: PropertyInfo): PropertyInfo { return { known: a.known && b.known, names: [...a.names, ...b.names], assignedNames: a.assignedNames.filter((name) => b.assignedNames.includes(name)), }; } + +function intersectPropertyInfo(a: PropertyInfo, b: PropertyInfo): PropertyInfo { + return { + known: a.known && b.known, + names: [...a.names, ...b.names], + assignedNames: [...a.assignedNames, ...b.assignedNames], + }; +} diff --git a/packages/mimir/test/no-duplicate-spread-property/test.ts b/packages/mimir/test/no-duplicate-spread-property/test.ts index 488d1716f..b87bf0063 100644 --- a/packages/mimir/test/no-duplicate-spread-property/test.ts +++ b/packages/mimir/test/no-duplicate-spread-property/test.ts @@ -232,3 +232,34 @@ var v: any; get foo() { return 2; }, set foo(v: number) {}, }); + +({ + foo: 1, + bar: 1, + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + bas: 1, +}); + +({ + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + foo: 1, + bar: 1, + bas: 1, +}); + +({ + ...get<{bar: number} & Record<'foo' | 'baz', number>>(), + foo: 1, + bar: 1, + baz: 1, +}); + +({ + bar: 1, + ...get<{bar: number} & Record>(), +}); + +({ + ...get<{bar: number} & Record>(), + bar: 1, +}); From 3ccce04496a01147c8f96acaf53e5c90b826c1f8 Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Tue, 30 Oct 2018 20:37:56 +0100 Subject: [PATCH 2/2] make test compatible with ts2.8 --- .../test/no-duplicate-spread-property/default/test.ts.lint | 4 ++-- .../test/no-duplicate-spread-property/loose/test.ts.lint | 4 ++-- packages/mimir/test/no-duplicate-spread-property/test.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint b/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint index cab989682..0f42c8595 100644 --- a/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint +++ b/baselines/packages/mimir/test/no-duplicate-spread-property/default/test.ts.lint @@ -288,10 +288,10 @@ var v: any; ({ bar: 1, ~~~ [error no-duplicate-spread-property: Property 'bar' is overridden later.] - ...get<{bar: number} & Record>(), + ...get<{bar: number} & Record>(), }); ({ - ...get<{bar: number} & Record>(), + ...get<{bar: number} & Record>(), bar: 1, }); diff --git a/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint b/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint index b87bf0063..44fd426e2 100644 --- a/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint +++ b/baselines/packages/mimir/test/no-duplicate-spread-property/loose/test.ts.lint @@ -256,10 +256,10 @@ var v: any; ({ bar: 1, - ...get<{bar: number} & Record>(), + ...get<{bar: number} & Record>(), }); ({ - ...get<{bar: number} & Record>(), + ...get<{bar: number} & Record>(), bar: 1, }); diff --git a/packages/mimir/test/no-duplicate-spread-property/test.ts b/packages/mimir/test/no-duplicate-spread-property/test.ts index b87bf0063..44fd426e2 100644 --- a/packages/mimir/test/no-duplicate-spread-property/test.ts +++ b/packages/mimir/test/no-duplicate-spread-property/test.ts @@ -256,10 +256,10 @@ var v: any; ({ bar: 1, - ...get<{bar: number} & Record>(), + ...get<{bar: number} & Record>(), }); ({ - ...get<{bar: number} & Record>(), + ...get<{bar: number} & Record>(), bar: 1, });