|
54 | 54 | * }
|
55 | 55 | * }
|
56 | 56 | */
|
57 |
| -export type ErrorList<T> = { [code: string]: string }; |
| 57 | +export type ErrorList<T extends string = string> = { |
| 58 | + readonly [K in T]: string |
| 59 | +}; |
58 | 60 |
|
59 | 61 | const ERROR_NAME = 'FirebaseError';
|
60 | 62 |
|
@@ -87,11 +89,10 @@ export interface FirebaseError {
|
87 | 89 | }
|
88 | 90 |
|
89 | 91 | export class FirebaseError implements FirebaseError {
|
90 |
| - public stack: string; |
91 |
| - public name: string; |
| 92 | + stack: string; |
| 93 | + name: string; |
92 | 94 |
|
93 | 95 | constructor(public code: string, public message: string) {
|
94 |
| - let stack: string; |
95 | 96 | // We want the stack value, if implemented by Error
|
96 | 97 | if (captureStackTrace) {
|
97 | 98 | // Patches this.stack, omitted calls above ErrorFactory#create
|
@@ -119,39 +120,46 @@ FirebaseError.prototype = Object.create(Error.prototype) as FirebaseError;
|
119 | 120 | FirebaseError.prototype.constructor = FirebaseError;
|
120 | 121 | (FirebaseError.prototype as any).name = ERROR_NAME;
|
121 | 122 |
|
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 | +> { |
123 | 129 | // Matches {$name}, by default.
|
124 |
| - public pattern = /\{\$([^}]+)}/g; |
| 130 | + pattern = /\{\$([^}]+)}/g; |
125 | 131 |
|
126 | 132 | constructor(
|
127 | 133 | private service: string,
|
128 | 134 | private serviceName: string,
|
129 |
| - private errors: ErrorList<T> |
| 135 | + private errors: ErrorList<ErrorCode> |
130 | 136 | ) {
|
131 | 137 | // empty
|
132 | 138 | }
|
133 | 139 |
|
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]; |
140 | 148 |
|
141 |
| - let fullCode = this.service + '/' + code; |
| 149 | + let fullCode = `${this.service}/${code}`; |
142 | 150 | let message: string;
|
143 | 151 |
|
144 | 152 | if (template === undefined) {
|
145 | 153 | message = 'Error';
|
146 | 154 | } 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}?>`; |
150 | 158 | });
|
151 | 159 | }
|
152 | 160 |
|
153 | 161 | // Service: Error message (service/code).
|
154 |
| - message = this.serviceName + ': ' + message + ' (' + fullCode + ').'; |
| 162 | + message = `${this.serviceName}: ${message} (${fullCode}).`; |
155 | 163 | let err = new FirebaseError(fullCode, message);
|
156 | 164 |
|
157 | 165 | // Populate the Error object with message parts for programmatic
|
|
0 commit comments