Skip to content

Commit 90054b0

Browse files
committed
fix: make it compile with strictNullChecks set to true like in our config file
1 parent a65c8ba commit 90054b0

File tree

114 files changed

+541
-296
lines changed

Some content is hidden

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

114 files changed

+541
-296
lines changed

package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,21 @@
3535
"scripts": {
3636
"start": "TZ=UTC node ./dist/server.js",
3737
"copy-templates": "copyfiles -u 1 src/mailtemplates/**/* dist/",
38-
"build:backend": "tsc --pretty --strictNullChecks false",
38+
"build:backend": "tsc --pretty",
3939
"build:frontend": "yarn --cwd ./frontend run build",
4040
"build:frontend:if-needed": "./scripts/build-frontend-if-needed.sh",
4141
"build": "yarn run clean && concurrently \"yarn:copy-templates\" \"yarn:build:frontend\" \"yarn:build:backend\"",
42-
"dev:backend": "TZ=UTC NODE_ENV=development tsc-watch --strictNullChecks false --onSuccess \"node dist/server-dev.js\"",
42+
"dev:backend": "TZ=UTC NODE_ENV=development tsc-watch --onSuccess \"node dist/server-dev.js\"",
4343
"dev:frontend": "wait-on tcp:4242 && yarn --cwd ./frontend run dev",
4444
"dev:frontend:cloud": "UNLEASH_BASE_PATH=/demo/ yarn run dev:frontend",
4545
"dev": "concurrently \"yarn:dev:backend\" \"yarn:dev:frontend\"",
4646
"prepare:backend": "concurrently \"yarn:copy-templates\" \"yarn:build:backend\"",
47-
"start:dev": "yarn run clean && TZ=UTC NODE_ENV=development tsc-watch --strictNullChecks false --onSuccess \"node dist/server-dev.js\"",
47+
"start:dev": "yarn run clean && TZ=UTC NODE_ENV=development tsc-watch --onSuccess \"node dist/server-dev.js\"",
4848
"db-migrate": "db-migrate --migrations-dir ./src/migrations",
4949
"lint": "biome check .",
5050
"lint:fix": "biome check . --write",
5151
"local:package": "del-cli --force build && mkdir build && cp -r dist docs CHANGELOG.md LICENSE README.md package.json build",
52-
"build:watch": "yarn run clean && tsc -w --strictNullChecks false",
52+
"build:watch": "tsc -w",
5353
"prepare": "husky && yarn --cwd ./frontend install && if [ ! -d ./dist ]; then yarn build; fi",
5454
"test": "NODE_ENV=test PORT=4243 node --trace-warnings node_modules/.bin/jest",
5555
"test:unit": "NODE_ENV=test PORT=4243 jest --testPathIgnorePatterns=src/test/e2e --testPathIgnorePatterns=dist",
@@ -60,7 +60,7 @@
6060
"test:coverage": "NODE_ENV=test PORT=4243 jest --coverage --testLocationInResults --outputFile=\"coverage/report.json\" --forceExit",
6161
"test:coverage:jest": "NODE_ENV=test PORT=4243 jest --silent --ci --json --coverage --testLocationInResults --outputFile=\"report.json\" --forceExit",
6262
"test:updateSnapshot": "NODE_ENV=test PORT=4243 jest --updateSnapshot",
63-
"seed:setup": "ts-node --compilerOptions '{\"strictNullChecks\": false}' src/test/e2e/seed/segment.seed.ts",
63+
"seed:setup": "ts-node src/test/e2e/seed/segment.seed.ts",
6464
"seed:serve": "UNLEASH_DATABASE_NAME=unleash_test UNLEASH_DATABASE_SCHEMA=seed yarn run start:dev",
6565
"clean": "del-cli --force dist",
6666
"heroku-postbuild": "cd frontend && yarn && yarn build",
@@ -220,7 +220,7 @@
220220
"supertest": "7.0.0",
221221
"ts-node": "10.9.2",
222222
"tsc-watch": "6.2.1",
223-
"typescript": "5.4.5",
223+
"typescript": "5.8.2",
224224
"wait-on": "^7.2.0"
225225
},
226226
"resolutions": {

src/lib/addons/datadog.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ interface DDRequestBody {
3232
export default class DatadogAddon extends Addon {
3333
private msgFormatter: FeatureEventFormatter;
3434

35-
flagResolver: IFlagResolver;
35+
declare flagResolver: IFlagResolver;
3636

3737
constructor(config: IAddonConfig) {
3838
super(definition, config);

src/lib/addons/new-relic.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ interface INewRelicRequestBody {
3939
export default class NewRelicAddon extends Addon {
4040
private msgFormatter: FeatureEventFormatter;
4141

42-
flagResolver: IFlagResolver;
42+
declare flagResolver: IFlagResolver;
4343

4444
constructor(config: IAddonConfig) {
4545
super(definition, config);

src/lib/addons/slack-app.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ interface ISlackAppAddonParameters {
3434
export default class SlackAppAddon extends Addon {
3535
private msgFormatter: FeatureEventFormatter;
3636

37-
flagResolver: IFlagResolver;
37+
declare flagResolver: IFlagResolver;
3838

3939
private accessToken?: string;
4040

src/lib/addons/slack.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ interface ISlackAddonParameters {
2525
export default class SlackAddon extends Addon {
2626
private msgFormatter: FeatureEventFormatter;
2727

28-
flagResolver: IFlagResolver;
28+
declare flagResolver: IFlagResolver;
2929

3030
constructor(args: IAddonConfig) {
3131
super(slackDefinition, args);

src/lib/addons/teams.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ interface ITeamsParameters {
3838
export default class TeamsAddon extends Addon {
3939
private msgFormatter: FeatureEventFormatter;
4040

41-
flagResolver: IFlagResolver;
41+
declare flagResolver: IFlagResolver;
4242

4343
constructor(args: IAddonConfig) {
4444
if (args.flagResolver.isEnabled('teamsIntegrationChangeRequests')) {

src/lib/addons/webhook.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ interface IParameters {
2525
export default class Webhook extends Addon {
2626
private msgFormatter: FeatureEventFormatter;
2727

28-
flagResolver: IFlagResolver;
28+
declare flagResolver: IFlagResolver;
2929

3030
constructor(args: IAddonConfig) {
3131
super(definition, args);

src/lib/app.test.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ jest.mock(
1818
},
1919
);
2020

21-
const getApp = require('./app').default;
22-
21+
import getApp from './app';
2322
test('should not throw when valid config', async () => {
2423
const config = createTestConfig();
24+
// @ts-expect-error - We're passing in empty stores and services
2525
const app = await getApp(config, {}, {});
2626
expect(typeof app.listen).toBe('function');
2727
});
@@ -33,6 +33,7 @@ test('should call preHook', async () => {
3333
called++;
3434
},
3535
});
36+
// @ts-expect-error - We're passing in empty stores and services
3637
await getApp(config, {}, {});
3738
expect(called).toBe(1);
3839
});
@@ -44,6 +45,7 @@ test('should call preRouterHook', async () => {
4445
called++;
4546
},
4647
});
48+
// @ts-expect-error - We're passing in empty stores and services
4749
await getApp(config, {}, {});
4850
expect(called).toBe(1);
4951
});
@@ -82,6 +84,7 @@ describe('compression middleware', () => {
8284
disableCompression: disableCompression as any,
8385
},
8486
});
87+
// @ts-expect-error - We're passing in empty stores and services
8588
await getApp(config, {}, {});
8689
expect(compression).toBeCalledTimes(+expectCompressionEnabled);
8790
},

src/lib/app.ts

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ export default async function getApp(
195195
}
196196

197197
// Setup API routes
198+
// @ts-expect-error - our db is possibly undefined and our indexrouter doesn't currently handle that
198199
app.use(`${baseUriPath}/`, new IndexRouter(config, services, db).router);
199200

200201
if (process.env.NODE_ENV !== 'production') {

src/lib/create-config.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -335,17 +335,22 @@ const parseEnvVarInitialAdminUser = (): UsernameAdminUser | undefined => {
335335
return username && password ? { username, password } : undefined;
336336
};
337337

338-
const defaultAuthentication: IAuthOption = {
339-
demoAllowAdminLogin: parseEnvVarBoolean(
340-
process.env.AUTH_DEMO_ALLOW_ADMIN_LOGIN,
341-
false,
342-
),
343-
enableApiToken: parseEnvVarBoolean(process.env.AUTH_ENABLE_API_TOKEN, true),
344-
type: authTypeFromString(process.env.AUTH_TYPE),
345-
customAuthHandler: defaultCustomAuthDenyAll,
346-
createAdminUser: true,
347-
initialAdminUser: parseEnvVarInitialAdminUser(),
348-
initApiTokens: [],
338+
const buildDefaultAuthOption = () => {
339+
return {
340+
demoAllowAdminLogin: parseEnvVarBoolean(
341+
process.env.AUTH_DEMO_ALLOW_ADMIN_LOGIN,
342+
false,
343+
),
344+
enableApiToken: parseEnvVarBoolean(
345+
process.env.AUTH_ENABLE_API_TOKEN,
346+
true,
347+
),
348+
type: authTypeFromString(process.env.AUTH_TYPE),
349+
customAuthHandler: defaultCustomAuthDenyAll,
350+
createAdminUser: true,
351+
initialAdminUser: parseEnvVarInitialAdminUser(),
352+
initApiTokens: [],
353+
};
349354
};
350355

351356
const defaultImport: WithOptional<IImportOption, 'file'> = {
@@ -563,7 +568,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
563568
const initApiTokens = loadInitApiTokens();
564569

565570
const authentication: IAuthOption = mergeAll([
566-
defaultAuthentication,
571+
buildDefaultAuthOption(),
567572
(options.authentication
568573
? removeUndefinedKeys(options.authentication)
569574
: options.authentication) || {},

src/lib/db/client-instance-store.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import type {
77
} from '../types/stores/client-instance-store';
88
import { subDays } from 'date-fns';
99
import type { Db } from './db';
10-
11-
const metricsHelper = require('../util/metrics-helper');
12-
const { DB_TIME } = require('../metric-events');
10+
import metricsHelper from '../util/metrics-helper';
11+
import { DB_TIME } from '../metric-events';
1312

1413
const COLUMNS = [
1514
'app_name',

src/lib/db/public-signup-token-store.ts

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ export class PublicSignupTokenStore implements IPublicSignupTokenStore {
153153
newToken: IPublicSignupTokenCreate,
154154
): Promise<PublicSignupTokenSchema> {
155155
const response = await this.db<ITokenRow>(TABLE).insert(
156+
// @ts-expect-error - knex expects us to return a DbRecordArr<OurType>, we return OurType, which works fine.
156157
toRow(newToken),
157158
['secret'],
158159
);

src/lib/db/user-feedback-store.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const fieldToRow = (fields: IUserFeedback): IUserFeedbackTable => ({
2525
});
2626

2727
const rowToField = (row: IUserFeedbackTable): IUserFeedback => ({
28-
neverShow: row.nevershow,
28+
neverShow: row.nevershow || false,
2929
feedbackId: row.feedback_id,
3030
given: row.given,
3131
userId: row.user_id,
@@ -71,7 +71,7 @@ export default class UserFeedbackStore implements IUserFeedbackStore {
7171
.merge()
7272
.returning(COLUMNS);
7373

74-
return rowToField(insertedFeedback[0]);
74+
return rowToField(insertedFeedback[0] as IUserFeedbackTable);
7575
}
7676

7777
async delete({ userId, feedbackId }: IUserFeedbackKey): Promise<void> {

src/lib/db/user-splash-store.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const fieldToRow = (fields: IUserSplash): IUserSplashTable => ({
2323
});
2424

2525
const rowToField = (row: IUserSplashTable): IUserSplash => ({
26-
seen: row.seen,
26+
seen: row.seen || false,
2727
splashId: row.splash_id,
2828
userId: row.user_id,
2929
});
@@ -65,7 +65,7 @@ export default class UserSplashStore implements IUserSplashStore {
6565
.merge()
6666
.returning(COLUMNS);
6767

68-
return rowToField(insertedSplash[0]);
68+
return rowToField(insertedSplash[0] as IUserSplashTable);
6969
}
7070

7171
async delete({ userId, splashId }: IUserSplashKey): Promise<void> {

src/lib/domain/project-health/project-health.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ const getPotentiallyStaleCount = (
2222
const today = new Date().valueOf();
2323

2424
return features.filter((feature) => {
25-
const diff = today - feature.createdAt?.valueOf();
25+
const diff = feature.createdAt
26+
? today - feature.createdAt.valueOf()
27+
: 0;
2628
const featureTypeExpectedLifetime = featureTypes.find(
2729
(t) => t.id === feature.type,
2830
)?.lifetimeDays;
2931

3032
return (
3133
!feature.stale &&
3234
featureTypeExpectedLifetime !== null &&
35+
featureTypeExpectedLifetime !== undefined &&
3336
diff >= featureTypeExpectedLifetime * hoursToMilliseconds(24)
3437
);
3538
}).length;

src/lib/features/client-feature-toggles/fakes/fake-client-feature-toggle-store.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default class FakeClientFeatureToggleStore
3838
...t,
3939
enabled: true,
4040
strategies: [],
41-
description: t.description || '',
41+
description: t.description,
4242
type: t.type || 'Release',
4343
stale: t.stale || false,
4444
variants: [],

src/lib/features/context/context-field-store.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const mapRow = (row: ContextFieldDB): IContextField => ({
5050

5151
interface ICreateContextField {
5252
name: string;
53-
description: string;
53+
description?: string | null;
5454
stickiness: boolean;
5555
sort_order: number;
5656
legal_values?: string;
@@ -80,8 +80,8 @@ class ContextFieldStore implements IContextFieldStore {
8080
return {
8181
name: data.name,
8282
description: data.description,
83-
stickiness: data.stickiness,
84-
sort_order: data.sortOrder, // eslint-disable-line
83+
stickiness: data.stickiness || false,
84+
sort_order: data.sortOrder || 0,
8585
legal_values: JSON.stringify(data.legalValues || []),
8686
};
8787
}

src/lib/features/context/context-service.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
import type { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType';
2222
import type EventService from '../events/event-service';
2323
import { contextSchema, legalValueSchema } from '../../services/context-schema';
24-
import { NameExistsError } from '../../error';
24+
import { NameExistsError, NotFoundError } from '../../error';
2525
import { nameSchema } from '../../schema/feature-schema';
2626
import type { LegalValueSchema } from '../../openapi';
2727

@@ -63,7 +63,13 @@ class ContextService {
6363
}
6464

6565
async getContextField(name: string): Promise<IContextField> {
66-
return this.contextFieldStore.get(name);
66+
const field = await this.contextFieldStore.get(name);
67+
if (field === undefined) {
68+
throw new NotFoundError(
69+
`Could not find context field with name ${name}`,
70+
);
71+
}
72+
return field;
6773
}
6874

6975
async getStrategiesByContextField(
@@ -125,6 +131,11 @@ class ContextService {
125131
const contextField = await this.contextFieldStore.get(
126132
updatedContextField.name,
127133
);
134+
if (contextField === undefined) {
135+
throw new NotFoundError(
136+
`Could not find context field with name: ${updatedContextField.name}`,
137+
);
138+
}
128139
const value = await contextSchema.validateAsync(updatedContextField);
129140

130141
await this.contextFieldStore.update(value);
@@ -147,6 +158,11 @@ class ContextService {
147158
const contextField = await this.contextFieldStore.get(
148159
contextFieldLegalValue.name,
149160
);
161+
if (contextField === undefined) {
162+
throw new NotFoundError(
163+
`Context field with name ${contextFieldLegalValue.name} was not found`,
164+
);
165+
}
150166
const validatedLegalValue = await legalValueSchema.validateAsync(
151167
contextFieldLegalValue.legalValue,
152168
);
@@ -186,6 +202,11 @@ class ContextService {
186202
const contextField = await this.contextFieldStore.get(
187203
contextFieldLegalValue.name,
188204
);
205+
if (contextField === undefined) {
206+
throw new NotFoundError(
207+
`Could not find context field with name ${contextFieldLegalValue.name}`,
208+
);
209+
}
189210

190211
const newContextField = {
191212
...contextField,

src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ beforeAll(async () => {
262262
contextFieldStore = db.stores.contextFieldStore;
263263

264264
const roles = await accessService.getRootRoles();
265-
adminRole = roles.find((role) => role.name === RoleName.ADMIN);
265+
adminRole = roles.find((role) => role.name === RoleName.ADMIN)!;
266266

267267
await createUserEditorAccess(
268268
regularUserName,

src/lib/features/export-import-toggles/export-import-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ export default class ExportImportService
463463
this.contextService.createContextField(
464464
{
465465
name: contextField.name,
466-
description: contextField.description || '',
466+
description: contextField.description,
467467
legalValues: contextField.legalValues,
468468
stickiness: contextField.stickiness,
469469
},

src/lib/features/export-import-toggles/import-permissions-service.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ export class ImportPermissionsService {
4848
): Promise<ContextFieldSchema[]> {
4949
const availableContextFields = await this.contextService.getAll();
5050

51-
return dto.data.contextFields?.filter(
52-
(contextField) =>
53-
!availableContextFields.some(
54-
(availableField) =>
55-
availableField.name === contextField.name,
56-
),
51+
return (
52+
dto.data.contextFields?.filter(
53+
(contextField) =>
54+
!availableContextFields.some(
55+
(availableField) =>
56+
availableField.name === contextField.name,
57+
),
58+
) || []
5759
);
5860
}
5961

0 commit comments

Comments
 (0)