Skip to content

Commit 9d576c8

Browse files
committed
feat(5.0.0): API Updates
5.0.0 brings changes which allows for better treeshaking and interopability with ES Modules. It also deprecates legacy class based APIs in favour of single purpose methods. - Removed default export. All methods avaliable as named export - Drop legacy Postcode class and documentation - Clean up typedocs - Fix ValidPostcode interface. SubDistrict can be null in some instances BREAKING CHANGE: - `postcode` no longer uses default exports. All exports are named - `postcode` no longer exports a class - Legacy `new Postcode()` functionality has been removed. Methods attached to `Postcode` are all available as named exports. E.g. `new Postcode(postcode).unit()` becomes `toUnit(postcode)`;
1 parent 0202b03 commit 9d576c8

File tree

6 files changed

+172
-298
lines changed

6 files changed

+172
-298
lines changed

lib/index.ts

Lines changed: 58 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
export const DISTRICT_SPLIT_REGEX = /^([a-z]{1,2}\d)([a-z])$/i;
2+
export const UNIT_REGEX = /[a-z]{2}$/i;
3+
export const INCODE_REGEX = /\d[a-z]{2}$/i;
4+
export const OUTCODE_REGEX = /^[a-z]{1,2}\d[a-z\d]?$/i;
5+
export const POSTCODE_REGEX = /^[a-z]{1,2}\d[a-z\d]?\s*\d[a-z]{2}$/i;
6+
export const AREA_REGEX = /^[a-z]{1,2}/i;
7+
18
interface Validator {
29
(input: string): boolean;
310
}
@@ -6,49 +13,17 @@ interface Parser {
613
(postcode: string): string | null;
714
}
815

9-
class ValidPostcode {
10-
private instance: Postcode;
11-
12-
constructor(postcode: string) {
13-
this.instance = new Postcode(postcode);
14-
}
15-
16-
get valid(): boolean {
17-
return this.instance.valid();
18-
}
19-
20-
get postcode(): string {
21-
return <string>this.instance.normalise();
22-
}
23-
24-
get incode(): string {
25-
return <string>this.instance.incode();
26-
}
27-
28-
get outcode(): string {
29-
return <string>this.instance.outcode();
30-
}
31-
32-
get area(): string {
33-
return <string>this.instance.area();
34-
}
35-
36-
get district(): string {
37-
return <string>this.instance.district();
38-
}
39-
40-
get subDistrict(): string {
41-
return <string>this.instance.subDistrict();
42-
}
43-
44-
get sector(): string {
45-
return <string>this.instance.sector();
46-
}
47-
48-
get unit(): string {
49-
return <string>this.instance.unit();
50-
}
51-
}
16+
type ValidPostcode = {
17+
valid: true;
18+
postcode: string;
19+
incode: string;
20+
outcode: string;
21+
area: string;
22+
district: string;
23+
subDistrict: string | null;
24+
sector: string;
25+
unit: string;
26+
};
5227

5328
type InvalidPostcode = {
5429
valid: false;
@@ -71,11 +46,12 @@ const invalidPostcode: InvalidPostcode = {
7146
district: null,
7247
subDistrict: null,
7348
sector: null,
74-
unit: null
49+
unit: null,
7550
};
7651

7752
/**
7853
* Return first elem of input is RegExpMatchArray or null if input null
54+
* @hidden
7955
*/
8056
const firstOrNull = (match: RegExpMatchArray | null): string | null => {
8157
if (match === null) return null;
@@ -86,17 +62,18 @@ const SPACE_REGEX = /\s+/gi;
8662

8763
/**
8864
* Drop all spaces and uppercase
65+
* @hidden
8966
*/
9067
const sanitize = (s: string): string =>
9168
s.replace(SPACE_REGEX, "").toUpperCase();
9269

70+
/**
71+
* Sanitizes string and returns regex matches
72+
* @hidden
73+
*/
9374
const matchOn = (s: string, regex: RegExp): RegExpMatchArray | null =>
9475
sanitize(s).match(regex);
9576

96-
const incodeRegex = /\d[a-z]{2}$/i;
97-
const validOutcodeRegex = /^[a-z]{1,2}\d[a-z\d]?$/i;
98-
const VALIDATION_REGEX = /^[a-z]{1,2}\d[a-z\d]?\s*\d[a-z]{2}$/i;
99-
10077
/**
10178
* Detects a "valid" postcode
10279
* - Starts and ends on a non-space character
@@ -109,15 +86,21 @@ const VALIDATION_REGEX = /^[a-z]{1,2}\d[a-z\d]?\s*\d[a-z]{2}$/i;
10986
* - AA9 9AA
11087
* - AA99 9AA
11188
*/
112-
const isValid: Validator = postcode =>
113-
postcode.match(VALIDATION_REGEX) !== null;
89+
export const isValid: Validator = (postcode) =>
90+
postcode.match(POSTCODE_REGEX) !== null;
91+
92+
/**
93+
* Returns true if string is a valid outcode
94+
*/
95+
export const validOutcode: Validator = (outcode) =>
96+
outcode.match(OUTCODE_REGEX) !== null;
11497

11598
/**
11699
* Returns a normalised postcode string (i.e. uppercased and properly spaced)
117100
*
118101
* Returns null if invalid postcode
119102
*/
120-
const toNormalised: Parser = postcode => {
103+
export const toNormalised: Parser = (postcode) => {
121104
const outcode = toOutcode(postcode);
122105
if (outcode === null) return null;
123106
const incode = toIncode(postcode);
@@ -130,30 +113,28 @@ const toNormalised: Parser = postcode => {
130113
*
131114
* Returns null if invalid postcode
132115
*/
133-
const toOutcode: Parser = postcode => {
116+
export const toOutcode: Parser = (postcode) => {
134117
if (!isValid(postcode)) return null;
135-
return sanitize(postcode).replace(incodeRegex, "");
118+
return sanitize(postcode).replace(INCODE_REGEX, "");
136119
};
137120

138121
/**
139122
* Returns a correctly formatted incode given a postcode
140123
*
141124
* Returns null if invalid postcode
142125
*/
143-
const toIncode: Parser = postcode => {
126+
export const toIncode: Parser = (postcode) => {
144127
if (!isValid(postcode)) return null;
145-
const match = matchOn(postcode, incodeRegex);
128+
const match = matchOn(postcode, INCODE_REGEX);
146129
return firstOrNull(match);
147130
};
148131

149-
const AREA_REGEX = /^[a-z]{1,2}/i;
150-
151132
/**
152133
* Returns a correctly formatted area given a postcode
153134
*
154135
* Returns null if invalid postcode
155136
*/
156-
const toArea: Parser = postcode => {
137+
export const toArea: Parser = (postcode) => {
157138
if (!isValid(postcode)) return null;
158139
const match = matchOn(postcode, AREA_REGEX);
159140
return firstOrNull(match);
@@ -164,29 +145,25 @@ const toArea: Parser = postcode => {
164145
*
165146
* Returns null if invalid postcode
166147
*/
167-
const toSector: Parser = postcode => {
148+
export const toSector: Parser = (postcode) => {
168149
const outcode = toOutcode(postcode);
169150
if (outcode === null) return null;
170151
const incode = toIncode(postcode);
171152
if (incode === null) return null;
172153
return `${outcode} ${incode[0]}`;
173154
};
174155

175-
const UNIT_REGEX = /[a-z]{2}$/i;
176-
177156
/**
178157
* Returns a correctly formatted unit given a postcode
179158
*
180159
* Returns null if invalid postcode
181160
*/
182-
const toUnit: Parser = postcode => {
161+
export const toUnit: Parser = (postcode) => {
183162
if (!isValid(postcode)) return null;
184163
const match = matchOn(postcode, UNIT_REGEX);
185164
return firstOrNull(match);
186165
};
187166

188-
const DISTRICT_SPLIT_REGEX = /^([a-z]{1,2}\d)([a-z])$/i;
189-
190167
/**
191168
* Returns a correctly formatted district given a postcode
192169
*
@@ -199,7 +176,7 @@ const DISTRICT_SPLIT_REGEX = /^([a-z]{1,2}\d)([a-z])$/i;
199176
* toDistrict("A9A 9AA") // => "A9"
200177
* ```
201178
*/
202-
const toDistrict: Parser = postcode => {
179+
export const toDistrict: Parser = (postcode) => {
203180
const outcode = toOutcode(postcode);
204181
if (outcode === null) return null;
205182
const match = outcode.match(DISTRICT_SPLIT_REGEX);
@@ -222,115 +199,28 @@ const toDistrict: Parser = postcode => {
222199
* toSubDistrict("A9 9AA") // => null
223200
* ```
224201
*/
225-
const toSubDistrict: Parser = postcode => {
202+
export const toSubDistrict: Parser = (postcode) => {
226203
const outcode = toOutcode(postcode);
227204
if (outcode === null) return null;
228205
const split = outcode.match(DISTRICT_SPLIT_REGEX);
229206
if (split === null) return null;
230207
return outcode;
231208
};
232209

233-
const returnNull = () => null;
234-
235210
/**
236-
* Postcode
237-
*
238-
* This wraps an input postcode string and provides instance methods to
239-
* validate, normalise or extract postcode data.
240-
*
241-
* This API is a bit more cumbersome that it needs to be. You should
242-
* favour `Postcode.parse()` or a static method depending on the
243-
* task at hand.
211+
* Returns a ValidPostcode or InvalidPostcode object from a postcode string
244212
*/
245-
class Postcode {
246-
private _raw: string;
247-
private _valid: boolean;
248-
private _incode?: string | null;
249-
private _outcode?: string | null;
250-
private _area?: string | null;
251-
private _unit?: string | null;
252-
private _district?: string | null;
253-
private _subDistrict?: string | null;
254-
private _sector?: string | null;
255-
256-
constructor(postcode: string) {
257-
this._raw = postcode;
258-
this._valid = isValid(postcode);
259-
260-
// All parse methods should return null if invalid
261-
if (!this._valid) {
262-
this.incode = returnNull;
263-
this.outcode = returnNull;
264-
this.area = returnNull;
265-
this.district = returnNull;
266-
this.subDistrict = returnNull;
267-
this.sector = returnNull;
268-
this.unit = returnNull;
269-
this.normalise = returnNull;
270-
}
271-
}
272-
273-
static isValid = isValid;
274-
static toNormalised = toNormalised;
275-
static toOutcode = toOutcode;
276-
static toIncode = toIncode;
277-
static toArea = toArea;
278-
static toSector = toSector;
279-
static toUnit = toUnit;
280-
static toDistrict = toDistrict;
281-
static toSubDistrict = toSubDistrict;
282-
283-
static validOutcode(outcode: string): boolean {
284-
return outcode.match(validOutcodeRegex) !== null;
285-
}
286-
287-
static parse(postcode: string): ValidPostcode | InvalidPostcode {
288-
if (isValid(postcode)) return new ValidPostcode(postcode);
289-
return { ...invalidPostcode };
290-
}
291-
292-
valid(): boolean {
293-
return this._valid;
294-
}
295-
296-
incode(): string | null {
297-
if (this._incode) return this._incode;
298-
return (this._incode = toIncode(this._raw));
299-
}
300-
301-
outcode(): string | null {
302-
if (this._outcode) return this._outcode;
303-
return (this._outcode = toOutcode(this._raw));
304-
}
305-
306-
area(): string | null {
307-
if (this._area) return this._area;
308-
return (this._area = toArea(this._raw));
309-
}
310-
311-
district(): string | null {
312-
if (this._district) return this._district;
313-
return (this._district = toDistrict(this._raw));
314-
}
315-
316-
subDistrict(): string | null {
317-
if (this._subDistrict) return this._subDistrict;
318-
return (this._subDistrict = toSubDistrict(this._raw));
319-
}
320-
321-
sector(): string | null {
322-
if (this._sector) return this._sector;
323-
return (this._sector = toSector(this._raw));
324-
}
325-
326-
unit(): string | null {
327-
if (this._unit) return this._unit;
328-
return (this._unit = toUnit(this._raw));
329-
}
330-
331-
normalise(): string | null {
332-
return `${this.outcode()} ${this.incode()}`;
333-
}
334-
}
335-
336-
export = Postcode;
213+
export const parse = (postcode: string): ValidPostcode | InvalidPostcode => {
214+
if (!isValid(postcode)) return { ...invalidPostcode };
215+
return {
216+
valid: true,
217+
postcode: toNormalised(postcode) as string,
218+
incode: toIncode(postcode) as string,
219+
outcode: toOutcode(postcode) as string,
220+
area: toArea(postcode) as string,
221+
district: toDistrict(postcode) as string,
222+
subDistrict: toSubDistrict(postcode),
223+
sector: toSector(postcode) as string,
224+
unit: toUnit(postcode) as string,
225+
};
226+
};

0 commit comments

Comments
 (0)