-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Improve types in Mortgage
and FairholdLandPurchase
#23
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
const DEFAULT_INTEREST_RATE = 0.06; | ||
const DEFAULT_MORTGAGE_TERM = 30; | ||
const DEFAULT_INITIAL_DEPOSIT = 0.15; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defining constants like this make it clearer that these are constants and not magic numbers, it should also make them easier to find and update 👍
type MortgageBreakdown = { | ||
yearlyPayment: number; | ||
cumulativePaid: number; | ||
remainingBalance: number; | ||
}[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Types and interfaces can be defined once, applied to their respective classes, and then cascade down to where needed.
* This value is given as a percentage. For example, 0.05 represents a 5% rate | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using JSDoc for meaningful comments leads to better DX as these are then accessible through most IDEs -
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's phenomenal! Super useful!
this.calculateMonthlyMortgagePayment(); // calculate the montly payment | ||
this.calculateYearlyPaymentBreakdown(); // calculate the yearly breakdown; | ||
this.termYears = mortgageTerm; | ||
this.principal = this.calculateMortgagePrinciple(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I was forgetting from our call earlier - as opposed to marking properties with !
which isn't ideal, as long as we define them in the constructor they'll get picked up as required properties.
@@ -0,0 +1 @@ | |||
export const MONTHS_PER_YEAR = 12; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is a bit redundant, discuss! For me it's a little easier to read and reason with than a mixture of numbers and variable names. If this isn't helpful we can revert to 12.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense! I imagine that saving this as a constant might help contextualise a *
or /
12
more quickly. One less jump to make while reading over some code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed - I think that parsing word * word
is generally easier than word * number
👍
newBuildPrice, // new build price of the property | ||
depreciatedBuildPrice, // depreciated building price | ||
constructionPriceGrowthPerYear, // construction price growth per year | ||
yearsForecast, // years forecast | ||
maintenanceCostPercentage, // maintenance cost percentage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a lot of similar comments removed in this PR - mainly as they add a bit of noise without much value, they're often a rehash of the variable name.
A good aim with comments is to try to explain "why", not "what". Hopefully the code and variable names should cover the "what" 👍
441c868
to
4798881
Compare
4798881
to
15b606c
Compare
interface MortgageParams { | ||
propertyValue: number; | ||
interestRate?: number; | ||
mortgageTerm?: number; | ||
initialDeposit?: number; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting up a type for the constructor params allows us to avoid a fair amount of type repetition / boilerplate. Still a bit verbose but a little better I think?
Likely required until a solution for microsoft/TypeScript#5326 is in place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I don't quite understand the ?
in the interface. I thought that all these parameters need to exist (can't be undefined) to instantiate the mortgage class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a much nice constructor, thanks for suggesting it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I can explain this!
These variables are genuinely optional in the constructor. If I don't provide them, then we'll fall back to the default values within the constructor (e.g. this.initialDeposit = params.initialDeposit || DEFAULT_INITIAL_DEPOSIT
).
This means we can easily construct a Mortgage
instance with only the required variable propertyValue
, and always have a class which has the required properties initialDepost
, interestRate
and termYears
.
yearsForecast: number, | ||
constructionPriceGrowthPerYear: number | ||
) { | ||
private calculateLifetime({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand why private
is used here--I get that it means its properties aren't accessible elsewhere, but why is that useful? Is that to prevent them from being modified or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep happy to explain this!
I'm using private
here just to annotate how the class is currently being used in the codebase. These functions are just internal to the class and being used to construct it. They don't have a use case (currently) outside the scope of the constructor.
This just means that when we're using an instance of the Mortgage
class downstream somewhere it won't be possible to call this function directly (mortgage.calculateLifetime()
, but we will be able to access the property mortgage.lifetime
.
app/models/Property.ts
Outdated
/** | ||
* Number of decimal places to use when rounding numerical values | ||
*/ | ||
const PRECISION = 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these be moved into constants.ts
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question! If we're consistently using two decimal places across the codebase as a standard, yes we should move it. If this is particular to this class, it can stay here.
app/models/Property.ts
Outdated
this.landToTotalRatio = this.landPrice / this.bedWeightedAveragePrice; // define the land to total ratio | ||
} | ||
} | ||
// TODO: Reuse HouseType enum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me see if got the logic right here. Since you previously defined the calculationSchema
, can we add something like this?
import { calculationSchema } from './calculationSchema';
type HouseType = typeof calculationSchema.shape.houseType._type;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure of the exact syntax, but that's essentially the idea yes. We would like to define the enum once, so that we don't need to try and synchronise various variables and make sure they always match.
We might define this in Zod first (as I did in another PR), we we might define it here in the class and pull this into our Zod validation schema.
Once both PRs are merged I'll come back and pick up this TODO 👍
15b606c
to
82705f9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the super clear explanations! Changes make a lot of sense and really tidy things up.
57ee5da
to
5386b54
Compare
What does this PR do
Mortgage
andFairholdLandPurchase
classes with the aim of removing properties marked as optional (?
) which are actually always present.