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==",
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",

View File

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

View File

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

View File

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

View File

@ -15,10 +15,10 @@ const pgp = pg();
// database connection (default to env vars)
const db = pgp({
'host': process.env.PGHOST,
'dbname': process.env.PGDATABASE,
'database': process.env.PGDATABASE,
'user': process.env.PGUSER,
'password': process.env.PGPASSWORD,
'port': process.env.PGPORT
'port': parseInt(process.env.PGPORT)
});
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
* 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) {
super(props);
// set building revision id, default 0
@ -130,7 +136,7 @@ class App extends React.Component {
colourBuilding(building) {
const cat = parseCategoryURL(window.location.pathname);
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'){
this.likeBuilding(building.building_id)
} 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
*/

View File

@ -47,17 +47,36 @@ BuildingEdit.propTypes = {
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) {
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 = {
error: this.props.error || undefined,
like: this.props.like || undefined,
copying: false,
keys_to_copy: {}
}
for (const field of props.fields) {
this.state[field.slug] = props[field.slug]
keys_to_copy: {},
...fieldsObj
}
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) => (
<Fragment>
<Label slug={props.slug} title={props.title} tooltip={props.tooltip}
@ -424,7 +429,20 @@ LongTextInput.propTypes = {
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) {
super(props);
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) => (
<Fragment>
<Label slug={props.slug} title={props.title} tooltip={props.tooltip}
@ -521,7 +526,7 @@ const TextListInput = (props) => (
id={props.slug} name={props.slug}
value={props.value || ''}
disabled={props.disabled}
list={`${props.slug}_suggestions`}
// list={`${props.slug}_suggestions`} TODO: investigate whether this was needed
onChange={props.handleChange}>
<option value="">Select a source</option>
{
@ -571,7 +576,22 @@ NumberInput.propTypes = {
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) {
super(props);
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) => (
<Fragment>
<Label slug={props.slug} title={props.title} tooltip={props.tooltip}
@ -675,7 +680,7 @@ LikeButton.propTypes = {
handleLike: PropTypes.func
}
const Label = (props) => {
const Label: React.SFC<any> = (props) => { // TODO: remove any
return (
<label htmlFor={props.slug}>
{props.title}

View File

@ -46,7 +46,18 @@ BuildingView.propTypes = {
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) {
super(props);
this.state = {
@ -194,18 +205,7 @@ class DataSection extends React.Component {
}
}
DataSection.propTypes = {
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) => {
const DataEntry: React.SFC<any> = (props) => { // TODO: remove any
return (
<Fragment>
<dt>
@ -241,9 +241,9 @@ DataEntry.propTypes = {
value: PropTypes.any
}
const LikeDataEntry = (props) => {
const LikeDataEntry: React.SFC<any> = (props) => { // TODO: remove any
const data_string = JSON.stringify({like: true});
(
return (
<Fragment>
<dt>
{ props.title }
@ -280,7 +280,7 @@ LikeDataEntry.propTypes = {
user_building_like: PropTypes.bool
}
const MultiDataEntry = (props) => {
const MultiDataEntry: React.SFC<any> = (props) => { // TODO: remove any
let content;
if (props.value && props.value.length) {

View File

@ -8,7 +8,13 @@ import './header.css';
/**
* 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) {
super(props);
this.state = {collapseMenu: true};
@ -106,10 +112,4 @@ class Header extends React.Component {
}
}
Header.propTypes = {
user: PropTypes.shape({
username: PropTypes.string
})
}
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) {
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;

View File

@ -6,7 +6,12 @@ import ErrorBox from './error-box';
import InfoBox from './info-box';
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) {
super(props);
this.state = {
@ -127,9 +132,4 @@ class Login extends Component {
}
}
Login.propTypes = {
login: PropTypes.func,
user: PropTypes.object
}
export default Login;

View File

@ -16,8 +16,16 @@ const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
/**
* 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) {
super(props);
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;

View File

@ -35,7 +35,7 @@ const MultiEdit = (props) => {
}
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);
return (
<Sidebar
@ -116,7 +116,7 @@ function fieldTitleFromSlug(slug) {
(prev, section) => {
const el = prev.concat(
section.fields.filter(
field => field.slug === slug
(field: any) => field.slug === slug // TODO: remove any
)
)
return el

View File

@ -4,7 +4,19 @@ import PropTypes from 'prop-types';
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) {
super(props);
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;

View File

@ -6,8 +6,12 @@ import { SearchIcon } from './icons';
/**
* 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) {
super(props);
this.state = {
@ -178,9 +182,4 @@ class SearchBox extends Component {
}
}
SearchBox.propTypes = {
onLocate: PropTypes.func,
isBuilding: PropTypes.bool
}
export default SearchBox;

View File

@ -6,7 +6,12 @@ import ErrorBox from './error-box';
import InfoBox from './info-box';
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) {
super(props);
this.state = {
@ -157,9 +162,4 @@ class SignUp extends Component {
}
}
SignUp.propTypes = {
login: PropTypes.func.isRequired,
user: PropTypes.object
}
export default SignUp;

View File

@ -4,7 +4,11 @@ import PropTypes from 'prop-types';
import './tooltip.css';
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) {
super(props);
this.state = {
@ -44,8 +48,4 @@ class Tooltip extends Component {
}
}
Tooltip.propTypes = {
text: PropTypes.string
}
export default Tooltip;

View File

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

View File

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