Skip to content

Commit fe286a6

Browse files
committed
Add types to ErrorFactory message parameters
1 parent 721ac1c commit fe286a6

File tree

2 files changed

+35
-22
lines changed

2 files changed

+35
-22
lines changed

packages/util/src/errors.ts

+26-18
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@
5454
* }
5555
* }
5656
*/
57-
export type ErrorList<T> = { [code: string]: string };
57+
export type ErrorList<T extends string = string> = {
58+
readonly [K in T]: string
59+
};
5860

5961
const ERROR_NAME = 'FirebaseError';
6062

@@ -87,11 +89,10 @@ export interface FirebaseError {
8789
}
8890

8991
export class FirebaseError implements FirebaseError {
90-
public stack: string;
91-
public name: string;
92+
stack: string;
93+
name: string;
9294

9395
constructor(public code: string, public message: string) {
94-
let stack: string;
9596
// We want the stack value, if implemented by Error
9697
if (captureStackTrace) {
9798
// Patches this.stack, omitted calls above ErrorFactory#create
@@ -119,39 +120,46 @@ FirebaseError.prototype = Object.create(Error.prototype) as FirebaseError;
119120
FirebaseError.prototype.constructor = FirebaseError;
120121
(FirebaseError.prototype as any).name = ERROR_NAME;
121122

122-
export class ErrorFactory<T extends string> {
123+
type AnyParams = { readonly [key: string]: StringLike | undefined };
124+
125+
export class ErrorFactory<
126+
ErrorCode extends string,
127+
ErrorParams extends Partial<{ readonly [K in ErrorCode]: AnyParams }> = {}
128+
> {
123129
// Matches {$name}, by default.
124-
public pattern = /\{\$([^}]+)}/g;
130+
pattern = /\{\$([^}]+)}/g;
125131

126132
constructor(
127133
private service: string,
128134
private serviceName: string,
129-
private errors: ErrorList<T>
135+
private errors: ErrorList<ErrorCode>
130136
) {
131137
// empty
132138
}
133139

134-
create(code: T, data?: { [prop: string]: StringLike }): FirebaseError {
135-
if (data === undefined) {
136-
data = {};
137-
}
138-
139-
let template = this.errors[code as string];
140+
create<K extends ErrorCode>(
141+
code: K,
142+
// For some reason, doesn't work with something like this to make the parameter optional only
143+
// if it's not defined in ErrorParams
144+
// data: ErrorParams[K] extends undefined ? AnyParams | void : ErrorParams[K]
145+
data?: ErrorParams[K] extends undefined ? AnyParams : ErrorParams[K]
146+
): FirebaseError {
147+
let template = this.errors[code];
140148

141-
let fullCode = this.service + '/' + code;
149+
let fullCode = `${this.service}/${code}`;
142150
let message: string;
143151

144152
if (template === undefined) {
145153
message = 'Error';
146154
} else {
147-
message = template.replace(this.pattern, (match, key) => {
148-
let value = data![key];
149-
return value !== undefined ? value.toString() : '<' + key + '?>';
155+
message = template.replace(this.pattern, (_, key) => {
156+
let value = data !== undefined ? data[key] : undefined;
157+
return value !== undefined ? value.toString() : `<${key}?>`;
150158
});
151159
}
152160

153161
// Service: Error message (service/code).
154-
message = this.serviceName + ': ' + message + ' (' + fullCode + ').';
162+
message = `${this.serviceName}: ${message} (${fullCode}).`;
155163
let err = new FirebaseError(fullCode, message);
156164

157165
// Populate the Error object with message parts for programmatic

packages/util/test/errors.test.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@ import { ErrorFactory, ErrorList, patchCapture } from '../src/errors';
1919

2020
type Err = 'generic-error' | 'file-not-found' | 'anon-replace';
2121

22-
let errors = {
22+
let errors: ErrorList<Err> = {
2323
'generic-error': 'Unknown error',
2424
'file-not-found': "Could not find file: '{$file}'",
2525
'anon-replace': 'Hello, {$repl_}!'
26-
} as ErrorList<Err>;
26+
};
2727

28-
let error = new ErrorFactory<Err>('fake', 'Fake', errors);
28+
interface ErrorParams {
29+
'file-not-found': { file: string };
30+
'anon-replace': { repl_: string };
31+
}
32+
33+
let error = new ErrorFactory<Err, ErrorParams>('fake', 'Fake', errors);
2934

3035
describe('FirebaseError', () => {
3136
it('create', () => {
@@ -59,7 +64,7 @@ describe('FirebaseError', () => {
5964
});
6065

6166
it('Missing replacement', () => {
62-
let e = error.create('file-not-found', { fileX: 'foo.txt' });
67+
let e = error.create('file-not-found', { fileX: 'foo.txt' } as any);
6368
assert.equal(e.code, 'fake/file-not-found');
6469
assert.equal(
6570
e.message,

0 commit comments

Comments
 (0)