Skip to content

Commit 247b1dc

Browse files
committed
Enable noUncheckedIndexedAccess
Mostly these asserts are: - asserting group in regex match - asserting first element, after checking length Other changes are: - Declaring lists as NonEmptyArray<T> - Using `asNonEmptyArray` to narrow to filled list, replacing `list.length === 0` checks
1 parent ec45704 commit 247b1dc

File tree

65 files changed

+268
-203
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+268
-203
lines changed

src/common/poll.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class PollResults<T> {
4848
/** Returns the unanimous vote, if there was one */
4949
get unanimous() {
5050
const all = this.sorted;
51-
return all.length === 1 ? all[0][0] : undefined;
51+
return all.length === 1 ? all[0]![0] : undefined;
5252
}
5353

5454
/** Returns all votes sorted by most voted first (ties are unaccounted for) */

src/common/required-when.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { groupBy } from '@seedcompany/common';
1+
import {
2+
asNonEmptyArray,
3+
groupBy,
4+
type NonEmptyArray,
5+
} from '@seedcompany/common';
26
import { createMetadataDecorator } from '@seedcompany/nest';
37
import { InputException } from './exceptions';
48
import { type ID } from './id-field';
@@ -50,7 +54,7 @@ RequiredWhen.calc = <TResourceStatic extends ResourceShape<any>>(
5054
const condition = RequiredWhenMetadata.get(resource, prop);
5155
return condition ? { ...condition, field: prop } : [];
5256
});
53-
const missing = conditions.flatMap((condition) => {
57+
const missingList = conditions.flatMap((condition) => {
5458
return condition.isEnabled(obj) &&
5559
(condition.isMissing?.(obj) ?? obj[condition.field] == null)
5660
? {
@@ -59,8 +63,10 @@ RequiredWhen.calc = <TResourceStatic extends ResourceShape<any>>(
5963
}
6064
: [];
6165
});
62-
if (missing.length > 0) {
63-
return new MissingRequiredFieldsException(res, { id: obj.id }, missing);
66+
const missing = asNonEmptyArray(missingList);
67+
if (missing) {
68+
const id = obj.id as ID;
69+
return new MissingRequiredFieldsException(res, { id }, missing);
6470
}
6571
return undefined;
6672
};
@@ -79,7 +85,7 @@ export class MissingRequiredFieldsException extends InputException {
7985
constructor(
8086
readonly resource: EnhancedResource<any>,
8187
readonly object: { id: ID },
82-
readonly missing: ReadonlyArray<{
88+
readonly missing: NonEmptyArray<{
8389
readonly field: string;
8490
readonly description: string;
8591
}>,

src/common/resource.dto.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CLASS_TYPE_METADATA, Field, InterfaceType } from '@nestjs/graphql';
22
import { type ClassType as ClassTypeVal } from '@nestjs/graphql/dist/enums/class-type.enum.js';
33
import {
4+
asNonEmptyArray,
45
cached,
56
type FnLike,
67
mapValues,
@@ -316,7 +317,7 @@ export class EnhancedResource<T extends ResourceShape<any>> {
316317
const declared = DbLabel.getOwn(cls);
317318
return declared ? [...declared] : [cls.name];
318319
});
319-
return [...new Set([...labels, 'BaseNode'])];
320+
return asNonEmptyArray([...new Set([...labels, 'BaseNode'])])!;
320321
}
321322
get dbLabel() {
322323
return this.dbLabels[0];

src/common/search-camel-case.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { startCase } from 'lodash';
2020
export function searchCamelCase<T extends string>(
2121
items: Iterable<T>,
2222
needle: string,
23-
) {
23+
): readonly T[] {
2424
const itemArr = [...items];
2525
const collator = new Intl.Collator('en');
2626
const fuzzy = new Fuzzy({
@@ -41,22 +41,22 @@ export function searchCamelCase<T extends string>(
4141
.sort(
4242
(ia, ib) =>
4343
// most contiguous chars matched
44-
chars[ib] - chars[ia] ||
44+
chars[ib]! - chars[ia]! ||
4545
// least char intra-fuzz (most contiguous)
46-
intraIns[ia] - intraIns[ib] ||
46+
intraIns[ia]! - intraIns[ib]! ||
4747
// earliest start of match
48-
start[ia] - start[ib] ||
48+
start[ia]! - start[ib]! ||
4949
// shortest match first
50-
haystack[idx[ia]].length - haystack[idx[ib]].length ||
50+
haystack[idx[ia]!]!.length - haystack[idx[ib]!]!.length ||
5151
// most prefix bounds, boosted by full term matches
52-
terms[ib] +
53-
interLft2[ib] +
54-
0.5 * interLft1[ib] -
55-
(terms[ia] + interLft2[ia] + 0.5 * interLft1[ia]) ||
52+
terms[ib]! +
53+
interLft2[ib]! +
54+
0.5 * interLft1[ib]! -
55+
(terms[ia]! + interLft2[ia]! + 0.5 * interLft1[ia]!) ||
5656
// the highest density of match (the least term inter-fuzz)
57-
interIns[ia] - interIns[ib] ||
57+
interIns[ia]! - interIns[ib]! ||
5858
// alphabetic
59-
collator.compare(haystack[idx[ia]], haystack[idx[ib]]),
59+
collator.compare(haystack[idx[ia]!]!, haystack[idx[ib]!]!),
6060
);
6161
},
6262
});
@@ -74,6 +74,6 @@ export function searchCamelCase<T extends string>(
7474
return [];
7575
}
7676

77-
const results = order.map((idx) => itemArr[indexes[idx]]);
77+
const results = order.map((idx) => itemArr[indexes[idx]!]!);
7878
return results;
7979
}

src/common/xlsx.util.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ export class WorkBook {
6262
if (!matched) {
6363
return SKIP;
6464
}
65-
const [_, sheetName, rangeStr] = matched;
65+
const sheetName = matched[1]!;
66+
const rangeStr = matched[2]!;
6667
try {
6768
const range = this.sheet(sheetName).range(rangeStr);
6869
return [name, range];
@@ -96,10 +97,11 @@ export class Sheet {
9697
this.book = arg;
9798
// @ts-expect-error yeah it's private this seems like the easiest way though
9899
this.workbook = this.book.book;
99-
this.sheet = this.workbook.Sheets[this.name];
100-
if (!this.sheet) {
100+
const sheet = this.workbook.Sheets[this.name];
101+
if (!sheet) {
101102
throw new NotFoundException(`Cannot find ${this.name} sheet`);
102103
}
104+
this.sheet = sheet;
103105
}
104106
nonEnumerable(this, 'workbook' as any, 'sheet' as any);
105107
}
@@ -233,8 +235,8 @@ export class Cell<TSheet extends Sheet = Sheet> {
233235
}
234236

235237
@Once() get hidden() {
236-
const colHidden = this.libSheet['!cols']?.[this.address.c].hidden ?? false;
237-
const rowHidden = this.libSheet['!rows']?.[this.address.r].hidden ?? false;
238+
const colHidden = this.libSheet['!cols']?.[this.address.c]?.hidden ?? false;
239+
const rowHidden = this.libSheet['!rows']?.[this.address.r]?.hidden ?? false;
238240
return colHidden || rowHidden;
239241
}
240242

src/components/authorization/policies/conditions/creator.condition.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Logger } from '@nestjs/common';
2+
import { type NonEmptyArray } from '@seedcompany/common';
23
import { inspect, type InspectOptionsStylized } from 'util';
34
import {
45
type ID,
@@ -58,11 +59,11 @@ class CreatorCondition<TResourceStatic extends ResourceShape<HasCreator>>
5859
return '.isCreator';
5960
}
6061

61-
union(this: void, conditions: this[]) {
62+
union(this: void, conditions: NonEmptyArray<this>) {
6263
return conditions[0];
6364
}
6465

65-
intersect(this: void, conditions: this[]) {
66+
intersect(this: void, conditions: NonEmptyArray<this>) {
6667
return conditions[0];
6768
}
6869

src/components/authorization/policies/conditions/enum-field.condition.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { groupBy } from '@seedcompany/common';
1+
import { groupBy, type NonEmptyArray } from '@seedcompany/common';
22
import { type Query } from 'cypher-query-builder';
33
import { get, startCase } from 'lodash';
44
import type { Get, Paths } from 'type-fest';
@@ -55,7 +55,7 @@ export class EnumFieldCondition<
5555
return '<str>' + eqlInLiteralSet(`.${this.path}`, this.allowed);
5656
}
5757

58-
union(conditions: this[]): Array<Condition<TResourceStatic>> {
58+
union(this: void, conditions: NonEmptyArray<this>) {
5959
return groupBy(conditions, (c) => c.path).map((conditionsForField) => {
6060
const unioned = conditionsForField.flatMap((c) => [...c.allowed]);
6161
return new EnumFieldCondition(
@@ -66,7 +66,7 @@ export class EnumFieldCondition<
6666
});
6767
}
6868

69-
intersect(conditions: this[]): Array<Condition<TResourceStatic>> {
69+
intersect(this: void, conditions: NonEmptyArray<this>) {
7070
return groupBy(conditions, (c) => c.path).map((conditionsForField) => {
7171
const intersected = [...conditionsForField[0].allowed].filter((v) =>
7272
conditionsForField.every((condition) => condition.allowed.has(v)),

src/components/authorization/policies/conditions/member.condition.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { type NonEmptyArray } from '@seedcompany/common';
12
import { type Query } from 'cypher-query-builder';
23
import { intersection } from 'lodash';
34
import { inspect, type InspectOptionsStylized } from 'util';
@@ -50,11 +51,11 @@ class MemberCondition<TResourceStatic extends ResourceWithScope>
5051
return resource.isEmbedded ? 'isMember' : '.isMember';
5152
}
5253

53-
union(this: void, conditions: this[]) {
54+
union(this: void, conditions: NonEmptyArray<this>) {
5455
return conditions[0];
5556
}
5657

57-
intersect(this: void, conditions: this[]) {
58+
intersect(this: void, conditions: NonEmptyArray<this>) {
5859
return conditions[0];
5960
}
6061

src/components/authorization/policies/conditions/role-and-exp-union.optimizer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { groupBy } from '@seedcompany/common';
1+
import { asNonEmptyArray, groupBy } from '@seedcompany/common';
22
import { mapOrElse } from '~/common';
33
import {
44
all,
@@ -43,8 +43,8 @@ export class RoleAndExpUnionOptimizer implements Optimizer {
4343
}
4444

4545
const merged = groupedByX.map((group) => {
46-
const { role, x } = group[0]!;
47-
const roles = role.union(group.map((c) => c.role));
46+
const { role, x } = group[0];
47+
const roles = role.union(asNonEmptyArray(group.map((c) => c.role))!);
4848
return all(roles, x);
4949
});
5050

@@ -65,7 +65,7 @@ export class RoleAndExpUnionOptimizer implements Optimizer {
6565
return null;
6666
}
6767
const role = condition.conditions[roleIdx] as RoleCondition;
68-
const x = condition.conditions[roleIdx === 0 ? 1 : 0];
68+
const x = condition.conditions[roleIdx === 0 ? 1 : 0]!;
6969
const xId = Condition.id(x);
7070
return { role, x, xId };
7171
}

src/components/authorization/policies/conditions/role.condition.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { type NonEmptyArray } from '@seedcompany/common';
12
import { inspect } from 'util';
23
import { type Role } from '~/common';
34
import {
@@ -27,12 +28,12 @@ export class RoleCondition implements Condition {
2728
return eqlDoesIntersect(currentRoles, this.allowed, roleType);
2829
}
2930

30-
union(this: void, conditions: this[]) {
31+
union(this: void, conditions: NonEmptyArray<this>) {
3132
const roles = conditions.flatMap((cond) => [...cond.allowed]);
3233
return new RoleCondition(new Set(roles));
3334
}
3435

35-
intersect(this: void, conditions: this[]) {
36+
intersect(this: void, conditions: NonEmptyArray<this>) {
3637
const intersected = [...conditions[0].allowed].filter((v) =>
3738
conditions.every((cond) => cond.allowed.has(v)),
3839
);

0 commit comments

Comments
 (0)