Skip to content

Commit 566cfaa

Browse files
authored
refactor: Add stricter type checking (#2538)
1 parent dfbbf8a commit 566cfaa

Some content is hidden

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

64 files changed

+520
-882
lines changed

.npmignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
types/tests
1+
types/tests.ts
2+
types/eslint.config.mjs

eslint.config.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ module.exports = tseslint.config({
66
files: ['**/*.js', '**/*.ts'],
77
extends: [
88
eslint.configs.recommended,
9-
...tseslint.configs.recommended,
9+
...tseslint.configs.stylistic,
10+
...tseslint.configs.strict,
1011
],
1112
plugins: {
1213
'@typescript-eslint': tseslint.plugin,
@@ -34,6 +35,9 @@ module.exports = tseslint.config({
3435
"@typescript-eslint/no-var-requires": "off",
3536
"@typescript-eslint/no-non-null-assertion": "off",
3637
"@typescript-eslint/no-require-imports": "off",
38+
"@typescript-eslint/no-dynamic-delete": "off",
39+
"@typescript-eslint/prefer-for-of": "off",
40+
"@typescript-eslint/no-extraneous-class": "off",
3741
"@typescript-eslint/no-unused-vars": [
3842
"error",
3943
{

integration/test/ParseUserTest.js

-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ const { v4: uuidv4 } = require('uuid');
66
const { twitterAuthData } = require('./helper');
77

88
class CustomUser extends Parse.User {
9-
constructor(attributes) {
10-
super(attributes);
11-
}
12-
139
doSomething() {
1410
return 5;
1511
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
"posttest:mongodb": "mongodb-runner stop --all",
109109
"lint": "eslint --cache src/ integration/",
110110
"lint:fix": "eslint --fix --cache src/ integration/",
111-
"test:types": "eslint types/tests.ts -c eslint.config.test.mjs",
111+
"test:types": "eslint types/tests.ts -c ./types/eslint.config.mjs",
112112
"watch": "cross-env PARSE_BUILD=${PARSE_BUILD} gulp watch",
113113
"watch:browser": "cross-env PARSE_BUILD=browser npm run watch",
114114
"watch:node": "cross-env PARSE_BUILD=node npm run watch",

src/Analytics.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import CoreManager from './CoreManager';
4040
* @returns {Promise} A promise that is resolved when the round-trip
4141
* to the server completes.
4242
*/
43-
export function track(name: string, dimensions: { [key: string]: string }): Promise<void> {
43+
export function track(name: string, dimensions: Record<string, string>): Promise<void> {
4444
name = name || '';
4545
name = name.replace(/^\s*/, '');
4646
name = name.replace(/\s*$/, '');
@@ -58,7 +58,7 @@ export function track(name: string, dimensions: { [key: string]: string }): Prom
5858
}
5959

6060
const DefaultController = {
61-
track(name: string, dimensions: { [key: string]: string }) {
61+
track(name: string, dimensions: Record<string, string>) {
6262
const path = 'events/' + name;
6363
const RESTController = CoreManager.getRESTController();
6464
return RESTController.request('POST', path, { dimensions });

src/CoreManager.ts

+63-70
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,25 @@ import type ParseConfig from './ParseConfig';
1515
import type LiveQueryClient from './LiveQueryClient';
1616
import type ParseInstallation from './ParseInstallation';
1717

18-
type AnalyticsController = {
19-
track: (name: string, dimensions: { [key: string]: string }) => Promise<any>;
20-
};
21-
type CloudController = {
18+
export interface AnalyticsController {
19+
track: (name: string, dimensions: Record<string, string>) => Promise<any>;
20+
}
21+
export interface CloudController {
2222
run: (name: string, data: any, options?: RequestOptions) => Promise<any>;
2323
getJobsData: (options?: RequestOptions) => Promise<any>;
2424
/** Returns promise which resolves with JobStatusId of the job */
2525
startJob: (name: string, data: any, options?: RequestOptions) => Promise<string>;
26-
};
27-
type ConfigController = {
26+
}
27+
export interface ConfigController {
2828
current: () => Promise<ParseConfig> | ParseConfig;
2929
get: (opts?: RequestOptions) => Promise<ParseConfig>;
30-
save: (
31-
attrs: { [key: string]: any },
32-
masterKeyOnlyFlags?: { [key: string]: any }
33-
) => Promise<void>;
34-
};
35-
type CryptoController = {
30+
save: (attrs: Record<string, any>, masterKeyOnlyFlags?: Record<string, any>) => Promise<void>;
31+
}
32+
export interface CryptoController {
3633
encrypt: (obj: any, secretKey: string) => string;
3734
decrypt: (encryptedText: string, secretKey: any) => string;
38-
};
39-
type FileController = {
35+
}
36+
export interface FileController {
4037
saveFile: (name: string, source: FileSource, options?: FullOptions) => Promise<any>;
4138
saveBase64: (
4239
name: string,
@@ -45,34 +42,34 @@ type FileController = {
4542
) => Promise<{ name: string; url: string }>;
4643
download: (uri: string, options?: any) => Promise<{ base64?: string; contentType?: string }>;
4744
deleteFile: (name: string, options?: { useMasterKey?: boolean }) => Promise<void>;
48-
};
49-
type InstallationController = {
45+
}
46+
export interface InstallationController {
5047
currentInstallationId: () => Promise<string>;
5148
currentInstallation: () => Promise<ParseInstallation | null>;
5249
updateInstallationOnDisk: (installation: ParseInstallation) => Promise<void>;
53-
};
54-
type ObjectController = {
50+
}
51+
export interface ObjectController {
5552
fetch: (
56-
object: ParseObject | Array<ParseObject>,
53+
object: ParseObject | ParseObject[],
5754
forceFetch: boolean,
5855
options?: RequestOptions
59-
) => Promise<Array<ParseObject | undefined> | ParseObject | undefined>;
56+
) => Promise<(ParseObject | undefined)[] | ParseObject | undefined>;
6057
save: (
61-
object: ParseObject | Array<ParseObject | ParseFile> | null,
58+
object: ParseObject | (ParseObject | ParseFile)[] | null,
6259
options?: RequestOptions
63-
) => Promise<ParseObject | Array<ParseObject> | ParseFile | undefined>;
60+
) => Promise<ParseObject | ParseObject[] | ParseFile | undefined>;
6461
destroy: (
65-
object: ParseObject | Array<ParseObject>,
62+
object: ParseObject | ParseObject[],
6663
options?: RequestOptions
67-
) => Promise<ParseObject | Array<ParseObject>>;
68-
};
69-
type ObjectStateController = {
64+
) => Promise<ParseObject | ParseObject[]>;
65+
}
66+
export interface ObjectStateController {
7067
getState: (obj: any) => State | null;
7168
initializeState: (obj: any, initial?: State) => State;
7269
removeState: (obj: any) => State | null;
7370
getServerData: (obj: any) => AttributeMap;
7471
setServerData: (obj: any, attributes: AttributeMap) => void;
75-
getPendingOps: (obj: any) => Array<OpsMap>;
72+
getPendingOps: (obj: any) => OpsMap[];
7673
setPendingOp: (obj: any, attr: string, op?: Op) => void;
7774
pushPendingState: (obj: any) => void;
7875
popPendingState: (obj: any) => OpsMap | undefined;
@@ -84,23 +81,19 @@ type ObjectStateController = {
8481
enqueueTask: (obj: any, task: () => Promise<void>) => Promise<void>;
8582
clearAllState: () => void;
8683
duplicateState: (source: any, dest: any) => void;
87-
};
88-
type PushController = {
84+
}
85+
export interface PushController {
8986
send: (data: PushData, options?: FullOptions) => Promise<any>;
90-
};
91-
type QueryController = {
87+
}
88+
export interface QueryController {
9289
find(
9390
className: string,
9491
params: QueryJSON,
9592
options?: RequestOptions
96-
): Promise<{ results?: Array<ParseObject>; className?: string; count?: number }>;
97-
aggregate(
98-
className: string,
99-
params: any,
100-
options?: RequestOptions
101-
): Promise<{ results?: Array<any> }>;
102-
};
103-
export type QueueObject = {
93+
): Promise<{ results?: ParseObject[]; className?: string; count?: number }>;
94+
aggregate(className: string, params: any, options?: RequestOptions): Promise<{ results?: any[] }>;
95+
}
96+
export interface QueueObject {
10497
queueId: string;
10598
action: string;
10699
object: ParseObject;
@@ -109,9 +102,9 @@ export type QueueObject = {
109102
className: string;
110103
hash: string;
111104
createdAt: Date;
112-
};
113-
export type Queue = Array<QueueObject>;
114-
export type EventuallyQueue = {
105+
}
106+
export type Queue = QueueObject[];
107+
export interface EventuallyQueue {
115108
save: (object: ParseObject, serverOptions?: SaveOptions) => Promise<void>;
116109
destroy: (object: ParseObject, serverOptions?: RequestOptions) => Promise<void>;
117110
generateQueueId: (action: string, object: ParseObject) => string;
@@ -138,8 +131,8 @@ export type EventuallyQueue = {
138131
byId(ObjectType: any, queueObject: any): Promise<void>;
139132
byHash(ObjectType: any, queueObject: any): Promise<void>;
140133
};
141-
};
142-
type RESTController = {
134+
}
135+
export interface RESTController {
143136
request: (method: string, path: string, data?: any, options?: RequestOptions) => Promise<any>;
144137
ajax: (
145138
method: string,
@@ -149,18 +142,18 @@ type RESTController = {
149142
options?: FullOptions
150143
) => Promise<any>;
151144
handleError: (err?: any) => void;
152-
};
153-
type SchemaController = {
145+
}
146+
export interface SchemaController {
154147
purge: (className: string) => Promise<any>;
155148
get: (className: string, options?: RequestOptions) => Promise<any>;
156149
delete: (className: string, options?: RequestOptions) => Promise<void>;
157150
create: (className: string, params: any, options?: RequestOptions) => Promise<any>;
158151
update: (className: string, params: any, options?: RequestOptions) => Promise<any>;
159152
send(className: string, method: string, params: any, options?: RequestOptions): Promise<any>;
160-
};
161-
type SessionController = {
153+
}
154+
export interface SessionController {
162155
getSession: (options?: RequestOptions) => Promise<ParseSession>;
163-
};
156+
}
164157
type StorageController =
165158
| {
166159
async: 0;
@@ -171,8 +164,8 @@ type StorageController =
171164
setItemAsync?: (path: string, value: string) => Promise<void>;
172165
removeItemAsync?: (path: string) => Promise<void>;
173166
clear: () => void;
174-
getAllKeys?: () => Array<string>;
175-
getAllKeysAsync?: () => Promise<Array<string>>;
167+
getAllKeys?: () => string[];
168+
getAllKeysAsync?: () => Promise<string[]>;
176169
}
177170
| {
178171
async: 1;
@@ -183,19 +176,19 @@ type StorageController =
183176
setItemAsync: (path: string, value: string) => Promise<void>;
184177
removeItemAsync: (path: string) => Promise<void>;
185178
clear: () => void;
186-
getAllKeys?: () => Array<string>;
187-
getAllKeysAsync?: () => Promise<Array<string>>;
179+
getAllKeys?: () => string[];
180+
getAllKeysAsync?: () => Promise<string[]>;
188181
};
189-
type LocalDatastoreController = {
182+
export interface LocalDatastoreController {
190183
fromPinWithName: (name: string) => any | undefined;
191184
pinWithName: (name: string, objects: any) => void;
192185
unPinWithName: (name: string) => void;
193186
getAllContents: () => any | undefined;
194187
clear: () => void;
195188
// Use for testing
196189
// getRawStorage(): Promise<Object>,
197-
};
198-
type UserController = {
190+
}
191+
export interface UserController {
199192
setCurrentUser: (user: ParseUser) => Promise<void>;
200193
currentUser: () => ParseUser | null;
201194
currentUserAsync: () => Promise<ParseUser | null>;
@@ -210,29 +203,29 @@ type UserController = {
210203
updateUserOnDisk: (user: ParseUser) => Promise<ParseUser>;
211204
upgradeToRevocableSession: (user: ParseUser, options?: RequestOptions) => Promise<void>;
212205
linkWith: (user: ParseUser, authData: AuthData, options?: FullOptions) => Promise<ParseUser>;
213-
removeUserFromDisk: () => Promise<ParseUser | void>;
206+
removeUserFromDisk: () => Promise<void>;
214207
verifyPassword: (
215208
username: string,
216209
password: string,
217210
options?: RequestOptions
218211
) => Promise<ParseUser>;
219212
requestEmailVerification: (email: string, options?: RequestOptions) => Promise<void>;
220-
};
221-
type HooksController = {
213+
}
214+
export interface HooksController {
222215
get: (type: string, functionName?: string, triggerName?: string) => Promise<any>;
223216
create: (hook: HookDeclaration) => Promise<any>;
224217
remove: (hook: HookDeleteArg) => Promise<any>;
225218
update: (hook: HookDeclaration) => Promise<any>;
226219
// Renamed to sendRequest since ParseHooks file & tests file uses this. (originally declared as just "send")
227220
sendRequest?: (method: string, path: string, body?: any) => Promise<any>;
228-
};
229-
type LiveQueryControllerType = {
221+
}
222+
export interface LiveQueryControllerType {
230223
setDefaultLiveQueryClient(liveQueryClient: LiveQueryClient): void;
231224
getDefaultLiveQueryClient(): Promise<LiveQueryClient>;
232225
_clearCachedDefaultClient(): void;
233-
};
226+
}
234227
/** Based on https://github.com/react-native-async-storage/async-storage/blob/main/packages/default-storage-backend/src/types.ts */
235-
type AsyncStorageType = {
228+
export interface AsyncStorageType {
236229
/** Fetches an item for a `key` and invokes a callback upon completion. */
237230
getItem: (
238231
key: string,
@@ -302,16 +295,16 @@ type AsyncStorageType = {
302295
keyValuePairs: [string, string][],
303296
callback?: (errors?: readonly (Error | null)[] | null) => void
304297
) => Promise<void>;
305-
};
306-
export type WebSocketController = {
298+
}
299+
export interface WebSocketController {
307300
onopen: () => void;
308301
onmessage: (message: any) => void;
309302
onclose: (arg?: any) => void;
310303
onerror: (error: any) => void;
311304
send: (data: any) => void;
312305
close: () => void;
313-
};
314-
type Config = {
306+
}
307+
interface Config {
315308
AnalyticsController?: AnalyticsController;
316309
CloudController?: CloudController;
317310
ConfigController?: ConfigController;
@@ -334,9 +327,9 @@ type Config = {
334327
) => WebSocketController;
335328
LiveQueryController?: LiveQueryControllerType;
336329
AsyncStorage?: AsyncStorageType;
337-
};
330+
}
338331

339-
const config: Config & { [key: string]: any } = {
332+
const config: Config & Record<string, any> = {
340333
IS_NODE:
341334
typeof process !== 'undefined' &&
342335
!!process.versions &&
@@ -364,7 +357,7 @@ const config: Config & { [key: string]: any } = {
364357
PARSE_ERRORS: [],
365358
};
366359

367-
function requireMethods(name: string, methods: Array<string>, controller: any) {
360+
function requireMethods(name: string, methods: string[], controller: any) {
368361
methods.forEach(func => {
369362
if (typeof controller[func] !== 'function') {
370363
throw new Error(`${name} must implement ${func}()`);

src/LocalDatastore.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const LocalDatastore = {
2828
isEnabled: false,
2929
isSyncing: false,
3030

31-
fromPinWithName(name: string): Promise<Array<any>> {
31+
fromPinWithName(name: string): Promise<any[]> {
3232
const controller = CoreManager.getLocalDatastoreController();
3333
return controller.fromPinWithName(name);
3434
},
@@ -61,7 +61,7 @@ const LocalDatastore = {
6161

6262
// Pin the object and children recursively
6363
// Saves the object and children key to Pin Name
64-
async _handlePinAllWithName(name: string, objects: Array<ParseObject>): Promise<void> {
64+
async _handlePinAllWithName(name: string, objects: ParseObject[]): Promise<void> {
6565
const pinName = this.getPinName(name);
6666
const toPinPromises = [];
6767
const objectKeys = [];
@@ -86,7 +86,7 @@ const LocalDatastore = {
8686

8787
// Removes object and children keys from pin name
8888
// Keeps the object and children pinned
89-
async _handleUnPinAllWithName(name: string, objects: Array<ParseObject>) {
89+
async _handleUnPinAllWithName(name: string, objects: ParseObject[]) {
9090
const localDatastore = await this._getAllContents();
9191
const pinName = this.getPinName(name);
9292
const promises = [];

src/LocalDatastoreController.default.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { isLocalDatastoreKey } from './LocalDatastoreUtils';
22
import Storage from './Storage';
33

44
const LocalDatastoreController = {
5-
async fromPinWithName(name: string): Promise<Array<any>> {
5+
async fromPinWithName(name: string): Promise<any[]> {
66
const values = await Storage.getItemAsync(name);
77
if (!values) {
88
return [];

src/LocalDatastoreController.react-native.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { isLocalDatastoreKey } from './LocalDatastoreUtils';
22
import RNStorage from './StorageController.react-native';
33

44
const LocalDatastoreController = {
5-
async fromPinWithName(name: string): Promise<Array<any>> {
5+
async fromPinWithName(name: string): Promise<any[]> {
66
const values = await RNStorage.getItemAsync(name);
77
if (!values) {
88
return [];

0 commit comments

Comments
 (0)