Squash TypeScript errors and fix bugs

Most errors highglighted by TS were due to a lack of type definitions
and were ignored by settings types to `any`.
Some minor bugs were resolved where the fix was obvious.
TODO marks left where `any` needs to be later removed or bugfix verified
This commit is contained in:
Maciej Ziarkowski 2019-08-09 18:49:43 +01:00
parent c92c4cded3
commit 4421930942
26 changed files with 169 additions and 153 deletions

15
app/package-lock.json generated
View File

@ -1054,6 +1054,21 @@
"integrity": "sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q==", "integrity": "sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q==",
"dev": true "dev": true
}, },
"@types/jest": {
"version": "24.0.17",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.17.tgz",
"integrity": "sha512-1cy3xkOAfSYn78dsBWy4M3h/QF/HeWPchNFDjysVtp3GHeTdSmtluNnELfCmfNRRHo0OWEcpf+NsEJQvwQfdqQ==",
"dev": true,
"requires": {
"@types/jest-diff": "20.0.1"
}
},
"@types/jest-diff": {
"version": "20.0.1",
"resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz",
"integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==",
"dev": true
},
"@types/mime": { "@types/mime": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",

View File

@ -38,7 +38,9 @@
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.0", "@types/express": "^4.17.0",
"@types/express-session": "^1.15.13", "@types/express-session": "^1.15.13",
"@types/jest": "^24.0.17",
"@types/node": "^12.7.1", "@types/node": "^12.7.1",
"@types/prop-types": "^15.7.1",
"@types/react": "^16.9.0", "@types/react": "^16.9.0",
"@types/react-dom": "^16.8.5", "@types/react-dom": "^16.8.5",
"@types/react-router-dom": "^4.3.4", "@types/react-router-dom": "^4.3.4",

View File

@ -72,7 +72,7 @@ function queryBuildingsByReference(key, id) {
return undefined; return undefined;
}); });
} }
return { error: 'Key must be UPRN or TOID' }; return Promise.resolve({ error: 'Key must be UPRN or TOID' });
} }
function getBuildingById(id) { function getBuildingById(id) {

View File

@ -8,7 +8,7 @@ import { hydrate } from 'react-dom';
import App from './frontend/app'; import App from './frontend/app';
const data = window.__PRELOADED_STATE__; const data = (window as any).__PRELOADED_STATE__; // TODO: remove any
hydrate( hydrate(
<BrowserRouter> <BrowserRouter>

View File

@ -15,10 +15,10 @@ const pgp = pg();
// database connection (default to env vars) // database connection (default to env vars)
const db = pgp({ const db = pgp({
'host': process.env.PGHOST, 'host': process.env.PGHOST,
'dbname': process.env.PGDATABASE, 'database': process.env.PGDATABASE,
'user': process.env.PGUSER, 'user': process.env.PGUSER,
'password': process.env.PGPASSWORD, 'password': process.env.PGPASSWORD,
'port': process.env.PGPORT 'port': parseInt(process.env.PGPORT)
}); });
export default db; export default db;

View File

@ -31,7 +31,13 @@ import { parseCategoryURL } from '../parse';
* map or other pages are rendered, based on the URL. Use a react-router-dom <Link /> in * map or other pages are rendered, based on the URL. Use a react-router-dom <Link /> in
* child components to navigate without a full page reload. * child components to navigate without a full page reload.
*/ */
class App extends React.Component { class App extends React.Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
user: PropTypes.object,
building: PropTypes.object,
building_like: PropTypes.bool
}
constructor(props) { constructor(props) {
super(props); super(props);
// set building revision id, default 0 // set building revision id, default 0
@ -130,7 +136,7 @@ class App extends React.Component {
colourBuilding(building) { colourBuilding(building) {
const cat = parseCategoryURL(window.location.pathname); const cat = parseCategoryURL(window.location.pathname);
const q = parse(window.location.search); const q = parse(window.location.search);
const data = (cat === 'like')? {like: true}: JSON.parse(q.data); const data = (cat === 'like')? {like: true}: JSON.parse(q.data as string); // TODO: verify what happens if data is string[]
if (cat === 'like'){ if (cat === 'like'){
this.likeBuilding(building.building_id) this.likeBuilding(building.building_id)
} else { } else {
@ -257,12 +263,6 @@ class App extends React.Component {
} }
} }
App.propTypes = {
user: PropTypes.object,
building: PropTypes.object,
building_like: PropTypes.bool
}
/** /**
* Component to fall back on in case of 404 or no other match * Component to fall back on in case of 404 or no other match
*/ */

View File

@ -47,17 +47,36 @@ BuildingEdit.propTypes = {
building_id: PropTypes.number building_id: PropTypes.number
} }
class EditForm extends Component { class EditForm extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
title: PropTypes.string,
slug: PropTypes.string,
cat: PropTypes.string,
help: PropTypes.string,
error: PropTypes.object,
like: PropTypes.bool,
building_like: PropTypes.bool,
selectBuilding: PropTypes.func,
building_id: PropTypes.number,
inactive: PropTypes.bool,
fields: PropTypes.array
};
constructor(props) { constructor(props) {
super(props); super(props);
// create object and spread into state to avoid TS complaining about modifying readonly state
let fieldsObj = {};
for (const field of props.fields) {
fieldsObj[field.slug] = props[field.slug];
}
this.state = { this.state = {
error: this.props.error || undefined, error: this.props.error || undefined,
like: this.props.like || undefined, like: this.props.like || undefined,
copying: false, copying: false,
keys_to_copy: {} keys_to_copy: {},
} ...fieldsObj
for (const field of props.fields) {
this.state[field.slug] = props[field.slug]
} }
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
@ -353,20 +372,6 @@ class EditForm extends Component {
} }
} }
EditForm.propTypes = {
title: PropTypes.string,
slug: PropTypes.string,
cat: PropTypes.string,
help: PropTypes.string,
error: PropTypes.object,
like: PropTypes.bool,
building_like: PropTypes.bool,
selectBuilding: PropTypes.func,
building_id: PropTypes.number,
inactive: PropTypes.bool,
fields: PropTypes.array
}
const TextInput = (props) => ( const TextInput = (props) => (
<Fragment> <Fragment>
<Label slug={props.slug} title={props.title} tooltip={props.tooltip} <Label slug={props.slug} title={props.title} tooltip={props.tooltip}
@ -424,7 +429,20 @@ LongTextInput.propTypes = {
handleChange: PropTypes.func handleChange: PropTypes.func
} }
class MultiTextInput extends Component { class MultiTextInput extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
slug: PropTypes.string,
title: PropTypes.string,
tooltip: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
placeholder: PropTypes.string,
disabled: PropTypes.bool,
handleChange: PropTypes.func,
copy: PropTypes.bool,
toggleCopyAttribute: PropTypes.func,
copying: PropTypes.bool
};
constructor(props) { constructor(props) {
super(props); super(props);
this.edit = this.edit.bind(this); this.edit = this.edit.bind(this);
@ -497,19 +515,6 @@ class MultiTextInput extends Component {
} }
} }
MultiTextInput.propTypes = {
slug: PropTypes.string,
title: PropTypes.string,
tooltip: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
placeholder: PropTypes.string,
disabled: PropTypes.bool,
handleChange: PropTypes.func,
copy: PropTypes.bool,
toggleCopyAttribute: PropTypes.func,
copying: PropTypes.bool
}
const TextListInput = (props) => ( const TextListInput = (props) => (
<Fragment> <Fragment>
<Label slug={props.slug} title={props.title} tooltip={props.tooltip} <Label slug={props.slug} title={props.title} tooltip={props.tooltip}
@ -521,7 +526,7 @@ const TextListInput = (props) => (
id={props.slug} name={props.slug} id={props.slug} name={props.slug}
value={props.value || ''} value={props.value || ''}
disabled={props.disabled} disabled={props.disabled}
list={`${props.slug}_suggestions`} // list={`${props.slug}_suggestions`} TODO: investigate whether this was needed
onChange={props.handleChange}> onChange={props.handleChange}>
<option value="">Select a source</option> <option value="">Select a source</option>
{ {
@ -571,7 +576,22 @@ NumberInput.propTypes = {
handleChange: PropTypes.func handleChange: PropTypes.func
} }
class YearEstimator extends Component { class YearEstimator extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
slug: PropTypes.string,
title: PropTypes.string,
tooltip: PropTypes.string,
date_year: PropTypes.number,
date_upper: PropTypes.number,
date_lower: PropTypes.number,
value: PropTypes.number,
disabled: PropTypes.bool,
handleChange: PropTypes.func,
copy: PropTypes.bool,
toggleCopyAttribute: PropTypes.func,
copying: PropTypes.bool
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -594,21 +614,6 @@ class YearEstimator extends Component {
} }
} }
YearEstimator.propTypes = {
slug: PropTypes.string,
title: PropTypes.string,
tooltip: PropTypes.string,
date_year: PropTypes.number,
date_upper: PropTypes.number,
date_lower: PropTypes.number,
value: PropTypes.number,
disabled: PropTypes.bool,
handleChange: PropTypes.func,
copy: PropTypes.bool,
toggleCopyAttribute: PropTypes.func,
copying: PropTypes.bool
}
const CheckboxInput = (props) => ( const CheckboxInput = (props) => (
<Fragment> <Fragment>
<Label slug={props.slug} title={props.title} tooltip={props.tooltip} <Label slug={props.slug} title={props.title} tooltip={props.tooltip}
@ -675,7 +680,7 @@ LikeButton.propTypes = {
handleLike: PropTypes.func handleLike: PropTypes.func
} }
const Label = (props) => { const Label: React.SFC<any> = (props) => { // TODO: remove any
return ( return (
<label htmlFor={props.slug}> <label htmlFor={props.slug}>
{props.title} {props.title}

View File

@ -46,7 +46,18 @@ BuildingView.propTypes = {
building_like: PropTypes.bool building_like: PropTypes.bool
} }
class DataSection extends React.Component { class DataSection extends React.Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
title: PropTypes.string,
cat: PropTypes.string,
slug: PropTypes.string,
intro: PropTypes.string,
help: PropTypes.string,
inactive: PropTypes.bool,
building_id: PropTypes.number,
children: PropTypes.node
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -194,18 +205,7 @@ class DataSection extends React.Component {
} }
} }
DataSection.propTypes = { const DataEntry: React.SFC<any> = (props) => { // TODO: remove any
title: PropTypes.string,
cat: PropTypes.string,
slug: PropTypes.string,
intro: PropTypes.string,
help: PropTypes.string,
inactive: PropTypes.bool,
building_id: PropTypes.number,
children: PropTypes.node
}
const DataEntry = (props) => {
return ( return (
<Fragment> <Fragment>
<dt> <dt>
@ -241,9 +241,9 @@ DataEntry.propTypes = {
value: PropTypes.any value: PropTypes.any
} }
const LikeDataEntry = (props) => { const LikeDataEntry: React.SFC<any> = (props) => { // TODO: remove any
const data_string = JSON.stringify({like: true}); const data_string = JSON.stringify({like: true});
( return (
<Fragment> <Fragment>
<dt> <dt>
{ props.title } { props.title }
@ -280,7 +280,7 @@ LikeDataEntry.propTypes = {
user_building_like: PropTypes.bool user_building_like: PropTypes.bool
} }
const MultiDataEntry = (props) => { const MultiDataEntry: React.SFC<any> = (props) => { // TODO: remove any
let content; let content;
if (props.value && props.value.length) { if (props.value && props.value.length) {

View File

@ -8,7 +8,13 @@ import './header.css';
/** /**
* Render the main header using a responsive design * Render the main header using a responsive design
*/ */
class Header extends React.Component { class Header extends React.Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
user: PropTypes.shape({
username: PropTypes.string
})
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {collapseMenu: true}; this.state = {collapseMenu: true};
@ -106,10 +112,4 @@ class Header extends React.Component {
} }
} }
Header.propTypes = {
user: PropTypes.shape({
username: PropTypes.string
})
}
export default Header; export default Header;

View File

@ -97,7 +97,12 @@ const LEGEND_CONFIG = {
}; };
class Legend extends React.Component { class Legend extends React.Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
slug: PropTypes.string,
color: PropTypes.string,
text: PropTypes.string
};
constructor(props) { constructor(props) {
super(props); super(props);
@ -195,11 +200,4 @@ class Legend extends React.Component {
} }
Legend.propTypes = {
slug: PropTypes.string,
color: PropTypes.string,
text: PropTypes.string
}
export default Legend; export default Legend;

View File

@ -6,7 +6,12 @@ import ErrorBox from './error-box';
import InfoBox from './info-box'; import InfoBox from './info-box';
import SupporterLogos from './supporter-logos'; import SupporterLogos from './supporter-logos';
class Login extends Component { class Login extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
login: PropTypes.func,
user: PropTypes.object
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -127,9 +132,4 @@ class Login extends Component {
} }
} }
Login.propTypes = {
login: PropTypes.func,
user: PropTypes.object
}
export default Login; export default Login;

View File

@ -16,8 +16,16 @@ const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
/** /**
* Map area * Map area
*/ */
class ColouringMap extends Component { class ColouringMap extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
building: PropTypes.object,
revision_id: PropTypes.number,
selectBuilding: PropTypes.func,
colourBuilding: PropTypes.func,
match: PropTypes.object,
history: PropTypes.object
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -167,13 +175,4 @@ class ColouringMap extends Component {
} }
} }
ColouringMap.propTypes = {
building: PropTypes.object,
revision_id: PropTypes.number,
selectBuilding: PropTypes.func,
colourBuilding: PropTypes.func,
match: PropTypes.object,
history: PropTypes.object
}
export default ColouringMap; export default ColouringMap;

View File

@ -35,7 +35,7 @@ const MultiEdit = (props) => {
} }
const q = parse(props.location.search); const q = parse(props.location.search);
const data = JSON.parse(q.data) const data = JSON.parse(q.data as string) // TODO: verify what happens when data is string[]
const title = sectionTitleFromCat(cat); const title = sectionTitleFromCat(cat);
return ( return (
<Sidebar <Sidebar
@ -116,7 +116,7 @@ function fieldTitleFromSlug(slug) {
(prev, section) => { (prev, section) => {
const el = prev.concat( const el = prev.concat(
section.fields.filter( section.fields.filter(
field => field.slug === slug (field: any) => field.slug === slug // TODO: remove any
) )
) )
return el return el

View File

@ -4,7 +4,19 @@ import PropTypes from 'prop-types';
import ErrorBox from './error-box'; import ErrorBox from './error-box';
class MyAccountPage extends Component { class MyAccountPage extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
user: PropTypes.shape({
username: PropTypes.string,
email: PropTypes.string,
registered: PropTypes.instanceOf(Date), // TODO: check if fix correct
api_key: PropTypes.string,
error: PropTypes.object
}),
updateUser: PropTypes.func,
logout: PropTypes.func
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -110,16 +122,4 @@ class MyAccountPage extends Component {
} }
} }
MyAccountPage.propTypes = {
user: PropTypes.shape({
username: PropTypes.string,
email: PropTypes.string,
registered: PropTypes.date,
api_key: PropTypes.string,
error: PropTypes.object
}),
updateUser: PropTypes.func,
logout: PropTypes.func
}
export default MyAccountPage; export default MyAccountPage;

View File

@ -6,8 +6,12 @@ import { SearchIcon } from './icons';
/** /**
* Search for location * Search for location
*/ */
class SearchBox extends Component { class SearchBox extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
onLocate: PropTypes.func,
isBuilding: PropTypes.bool
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -178,9 +182,4 @@ class SearchBox extends Component {
} }
} }
SearchBox.propTypes = {
onLocate: PropTypes.func,
isBuilding: PropTypes.bool
}
export default SearchBox; export default SearchBox;

View File

@ -6,7 +6,12 @@ import ErrorBox from './error-box';
import InfoBox from './info-box'; import InfoBox from './info-box';
import SupporterLogos from './supporter-logos'; import SupporterLogos from './supporter-logos';
class SignUp extends Component { class SignUp extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
login: PropTypes.func.isRequired,
user: PropTypes.object
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -157,9 +162,4 @@ class SignUp extends Component {
} }
} }
SignUp.propTypes = {
login: PropTypes.func.isRequired,
user: PropTypes.object
}
export default SignUp; export default SignUp;

View File

@ -4,7 +4,11 @@ import PropTypes from 'prop-types';
import './tooltip.css'; import './tooltip.css';
import { InfoIcon } from './icons'; import { InfoIcon } from './icons';
class Tooltip extends Component { class Tooltip extends Component<any, any> { // TODO: add proper types
static propTypes = { // TODO: generate propTypes from TS
text: PropTypes.string
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -44,8 +48,4 @@ class Tooltip extends Component {
} }
} }
Tooltip.propTypes = {
text: PropTypes.string
}
export default Tooltip; export default Tooltip;

View File

@ -10,12 +10,10 @@ const server = http.createServer(app);
let currentApp = app; let currentApp = app;
server.listen(process.env.PORT || 3000, error => { server.listen(process.env.PORT || 3000, () => {
if (error) {
console.log(error);
}
console.log('🚀 started'); console.log('🚀 started');
}).on('error', error => {
console.log(error);
}); });
// In development mode, enable hot module reloading (HMR) // In development mode, enable hot module reloading (HMR)

View File

@ -48,7 +48,7 @@ server.use(bodyParser.json());
// handle user sessions // handle user sessions
const pgSession = pgConnect(session); const pgSession = pgConnect(session);
const sess = { const sess: any = { // TODO: remove any
name: 'cl.session', name: 'cl.session',
store: new pgSession({ store: new pgSession({
pgPromise: db, pgPromise: db,
@ -72,8 +72,8 @@ server.get('/*.html', frontendRoute);
server.get('/', frontendRoute); server.get('/', frontendRoute);
function frontendRoute(req, res) { function frontendRoute(req, res) {
const context = {}; const context: any = {}; // TODO: remove any
const data = {}; const data: any = {}; // TODO: remove any
context.status = 200; context.status = 200;
const userId = req.session.user_id; const userId = req.session.user_id;
@ -339,7 +339,7 @@ server.post('/users', function (req, res) {
// POST user auth // POST user auth
server.post('/login', function (req, res) { server.post('/login', function (req, res) {
authUser(req.body.username, req.body.password).then(function (user) { authUser(req.body.username, req.body.password).then(function (user: any) { // TODO: remove any
if (user.user_id) { if (user.user_id) {
req.session.user_id = user.user_id; req.session.user_id = user.user_id;
} else { } else {