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+
18interface 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
5328type 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 */
8056const 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 */
9067const sanitize = ( s : string ) : string =>
9168 s . replace ( SPACE_REGEX , "" ) . toUpperCase ( ) ;
9269
70+ /**
71+ * Sanitizes string and returns regex matches
72+ * @hidden
73+ */
9374const 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